TensorFlow.js 人脸检测
本指南介绍了 TensorFlow.js 与 Docker 的无缝集成,以实现人脸检测。在本指南中,你将学习如何:
- 使用 Docker 运行容器化的 TensorFlow.js 应用。
- 在 Web 应用中使用 TensorFlow.js 实现人脸检测。
- 为 TensorFlow.js Web 应用构建 Dockerfile。
- 使用 Docker Compose 进行实时应用开发和更新。
- 在 Docker Hub 上分享你的 Docker 镜像,以促进部署和扩大影响范围。
致谢
Docker 要感谢 Harsh Manvar 对本指南的贡献。
前提条件
- 你已安装最新版本的 Docker Desktop。
- 你有一个 Git 客户端。本指南中的示例使用基于命令行的 Git 客户端,但你可以使用任何客户端。
什么是 TensorFlow.js?
TensorFlow.js 是一个开源 JavaScript 库,用于机器学习,可让你在浏览器或 Node.js 上训练和部署 ML 模型。它支持从头开始创建新模型或使用预训练模型,从而直接在 Web 环境中实现广泛的 ML 应用。TensorFlow.js 提供高效计算,使 Web 开发人员无需深入的 ML 专业知识即可进行复杂的 ML 任务。
为什么一起使用 TensorFlow.js 和 Docker?
- 环境一致性与简化部署:Docker 将 TensorFlow.js 应用及其依赖项打包到容器中,确保在所有环境中一致运行并简化部署。
- 高效开发与轻松扩展:Docker 通过热重载等功能提高开发效率,并利用 Kubernetes 等编排工具轻松扩展 TensorFlow.js 应用。
- 隔离与增强安全性:Docker 在安全环境中隔离 TensorFlow.js 应用,最大限度地减少冲突和安全漏洞,同时以有限权限运行应用。
获取并运行示例应用
在终端中,使用以下命令克隆示例应用。
$ git clone https://github.com/harsh4870/TensorJS-Face-Detection
克隆应用后,你会注意到应用有一个 Dockerfile
文件。这个 Dockerfile 可以让你仅使用 Docker 在本地构建和运行应用。
在将应用作为容器运行之前,必须将其构建成镜像。在 TensorJS-Face-Detection
目录中运行以下命令,构建一个名为 face-detection-tensorjs
的镜像。
$ docker build -t face-detection-tensorjs .
此命令将应用构建成镜像。第一次运行此命令时,根据你的网络连接情况,下载必要的组件可能需要几分钟。
要将镜像作为容器运行,请在终端中运行以下命令。
$ docker run -p 80:80 face-detection-tensorjs
此命令运行容器,并将容器中的 80 端口映射到你系统上的 80 端口。
应用运行后,打开 Web 浏览器访问 http://localhost:80。你可能需要授予应用访问摄像头的权限。
在 Web 应用中,你可以更改后端以使用以下之一:
- WASM
- WebGL
- CPU
要停止应用,请在终端中按 ctrl
+c
。
关于应用
示例应用使用 MediaPipe 执行实时人脸检测,MediaPipe 是一个用于构建多模态机器学习管道的综合框架。它特别使用了 BlazeFace 模型,这是一个用于检测图像中人脸的轻量级模型。
在 TensorFlow.js 或类似的基于 Web 的机器学习框架中,WASM、WebGL 和 CPU 后端可用于执行操作。每个后端都利用现代浏览器中可用的不同资源和技术,并有其优点和局限性。以下部分简要介绍了不同的后端。
WASM
WebAssembly (WASM) 是一种低级、类似汇编的语言,具有紧凑的二进制格式,可在 Web 浏览器中以接近原生速度运行。它允许将用 C/C++ 等语言编写的代码编译成可在浏览器中执行的二进制文件。
当需要高性能,并且 WebGL 后端不受支持或你希望在所有设备上获得一致的性能而不依赖 GPU 时,WASM 是一个不错的选择。
WebGL
WebGL 是一个浏览器 API,它允许在网页画布中使用 GPU 加速的物理和图像处理及效果。
WebGL 非常适合可并行化并能从 GPU 加速中显著受益的操作,例如深度学习模型中常见的矩阵乘法和卷积。
CPU
CPU 后端使用纯 JavaScript 执行,利用设备的中央处理器 (CPU)。此后端是通用兼容性最好的后端,当 WebGL 和 WASM 后端都不可用或不适合时,可作为备用。
浏览应用代码
在以下部分中浏览每个文件的用途及其内容。
index.html 文件
index.html
文件充当 Web 应用的前端,该应用利用 TensorFlow.js 从网络摄像头视频流中进行实时人脸检测。它集成了多种技术和库,以便直接在浏览器中进行机器学习。它使用了多个 TensorFlow.js 库,包括:
- tfjs-core 和 tfjs-converter,用于核心 TensorFlow.js 功能和模型转换。
- tfjs-backend-webgl、tfjs-backend-cpu 和 tf-backend-wasm 脚本,用于 TensorFlow.js 可用于处理的不同计算后端选项。这些后端允许应用利用用户的硬件能力高效地执行机器学习任务。
- BlazeFace 库,一个用于人脸检测的 TensorFlow 模型。
它还使用了以下额外的库:
- dat.GUI,用于创建一个图形界面,以便与应用的实时设置进行交互,例如在 TensorFlow.js 后端之间切换。
- Stats.min.js,用于显示性能指标(如 FPS),以监控应用在运行期间的效率。
<style>
body {
margin: 25px;
}
.true {
color: green;
}
.false {
color: red;
}
#main {
position: relative;
margin: 50px 0;
}
canvas {
position: absolute;
top: 0;
left: 0;
}
#description {
margin-top: 20px;
width: 600px;
}
#description-title {
font-weight: bold;
font-size: 18px;
}
</style>
<body>
<div id="main">
<video
id="video"
playsinline
style="
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
width: auto;
height: auto;
"
></video>
<canvas id="output"></canvas>
<video
id="video"
playsinline
style="
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
visibility: hidden;
width: auto;
height: auto;
"
></video>
</div>
</body>
<script src="https://unpkg.com/@tensorflow/tfjs-core@2.1.0/dist/tf-core.js"></script>
<script src="https://unpkg.com/@tensorflow/tfjs-converter@2.1.0/dist/tf-converter.js"></script>
<script src="https://unpkg.com/@tensorflow/tfjs-backend-webgl@2.1.0/dist/tf-backend-webgl.js"></script>
<script src="https://unpkg.com/@tensorflow/tfjs-backend-cpu@2.1.0/dist/tf-backend-cpu.js"></script>
<script src="./tf-backend-wasm.js"></script>
<script src="https://unpkg.com/@tensorflow-models/blazeface@0.0.5/dist/blazeface.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<script src="./index.js"></script>
index.js 文件
index.js
文件负责执行人脸检测逻辑。它展示了 Web 开发和机器学习集成中的几个高级概念。以下是其一些关键组件和功能的细分:
- Stats.js:脚本通过创建一个 Stats 实例开始,以实时监控和显示应用的帧率 (FPS)。这对于性能分析非常有用,尤其是在测试不同 TensorFlow.js 后端对应用速度的影响时。
- TensorFlow.js:应用允许用户通过 dat.GUI 提供的图形界面在 TensorFlow.js 的不同计算后端(wasm、webgl 和 cpu)之间切换。更改后端会根据设备和浏览器影响性能和兼容性。addFlagLabels 函数动态检查并显示是否支持 SIMD(单指令多数据)和多线程,这对于 wasm 后端的性能优化至关重要。
- setupCamera 函数:使用 MediaDevices Web API 初始化用户的网络摄像头。它配置视频流不包含音频,并使用前置摄像头 (facingMode: 'user')。视频元数据加载完成后,它会使用视频元素解析一个 Promise,然后该视频元素用于人脸检测。
- BlazeFace:此应用的核心是 renderPrediction 函数,它使用 BlazeFace 模型执行实时人脸检测。BlazeFace 模型是一个用于检测图像中人脸的轻量级模型。该函数在每个动画帧上调用 model.estimateFaces,以从视频流中检测人脸。对于每个检测到的人脸,它会在视频上方的画布上绘制一个红色矩形框出人脸,并用蓝色点标记面部特征点。
const stats = new Stats();
stats.showPanel(0);
document.body.prepend(stats.domElement);
let model, ctx, videoWidth, videoHeight, video, canvas;
const state = {
backend: "wasm",
};
const gui = new dat.GUI();
gui
.add(state, "backend", ["wasm", "webgl", "cpu"])
.onChange(async (backend) => {
await tf.setBackend(backend);
addFlagLables();
});
async function addFlagLables() {
if (!document.querySelector("#simd_supported")) {
const simdSupportLabel = document.createElement("div");
simdSupportLabel.id = "simd_supported";
simdSupportLabel.style = "font-weight: bold";
const simdSupported = await tf.env().getAsync("WASM_HAS_SIMD_SUPPORT");
simdSupportLabel.innerHTML = `SIMD supported: <span class=${simdSupported}>${simdSupported}<span>`;
document.querySelector("#description").appendChild(simdSupportLabel);
}
if (!document.querySelector("#threads_supported")) {
const threadSupportLabel = document.createElement("div");
threadSupportLabel.id = "threads_supported";
threadSupportLabel.style = "font-weight: bold";
const threadsSupported = await tf
.env()
.getAsync("WASM_HAS_MULTITHREAD_SUPPORT");
threadSupportLabel.innerHTML = `Threads supported: <span class=${threadsSupported}>${threadsSupported}</span>`;
document.querySelector("#description").appendChild(threadSupportLabel);
}
}
async function setupCamera() {
video = document.getElementById("video");
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: { facingMode: "user" },
});
video.srcObject = stream;
return new Promise((resolve) => {
video.onloadedmetadata = () => {
resolve(video);
};
});
}
const renderPrediction = async () => {
stats.begin();
const returnTensors = false;
const flipHorizontal = true;
const annotateBoxes = true;
const predictions = await model.estimateFaces(
video,
returnTensors,
flipHorizontal,
annotateBoxes,
);
if (predictions.length > 0) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < predictions.length; i++) {
if (returnTensors) {
predictions[i].topLeft = predictions[i].topLeft.arraySync();
predictions[i].bottomRight = predictions[i].bottomRight.arraySync();
if (annotateBoxes) {
predictions[i].landmarks = predictions[i].landmarks.arraySync();
}
}
const start = predictions[i].topLeft;
const end = predictions[i].bottomRight;
const size = [end[0] - start[0], end[1] - start[1]];
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
ctx.fillRect(start[0], start[1], size[0], size[1]);
if (annotateBoxes) {
const landmarks = predictions[i].landmarks;
ctx.fillStyle = "blue";
for (let j = 0; j < landmarks.length; j++) {
const x = landmarks[j][0];
const y = landmarks[j][1];
ctx.fillRect(x, y, 5, 5);
}
}
}
}
stats.end();
requestAnimationFrame(renderPrediction);
};
const setupPage = async () => {
await tf.setBackend(state.backend);
addFlagLables();
await setupCamera();
video.play();
videoWidth = video.videoWidth;
videoHeight = video.videoHeight;
video.width = videoWidth;
video.height = videoHeight;
canvas = document.getElementById("output");
canvas.width = videoWidth;
canvas.height = videoHeight;
ctx = canvas.getContext("2d");
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
model = await blazeface.load();
renderPrediction();
};
setupPage();
tf-backend-wasm.js 文件
tf-backend-wasm.js
文件是 TensorFlow.js 库 的一部分。它包含 TensorFlow.js WASM 后端的初始化逻辑、用于与 WASM 二进制文件交互的一些实用程序,以及设置 WASM 二进制文件自定义路径的函数。
tfjs-backend-wasm-simd.wasm 文件
tfjs-backend-wasm-simd.wasm
文件是 TensorFlow.js 库 的一部分。它是一个 WASM 二进制文件,用于 WebAssembly 后端,专门优化以利用 SIMD(单指令多数据)指令。
浏览 Dockerfile
在基于 Docker 的项目中,Dockerfile 是构建应用环境的基础资产。
Dockerfile 是一个文本文件,它指导 Docker 如何创建应用环境的镜像。镜像包含运行应用所需的一切,例如文件、软件包和工具。
以下是此项目的 Dockerfile。
FROM nginx:stable-alpine3.17-slim
WORKDIR /usr/share/nginx/html
COPY . .
此 Dockerfile 定义了一个镜像,该镜像使用 Nginx 从 Alpine Linux 基础镜像提供静态内容。
使用 Compose 进行开发
Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。使用 Compose,你使用 YAML 文件来配置应用的服务、网络和卷。在这种情况下,应用不是多容器应用,但 Docker Compose 还有其他对开发有用的功能,例如 Compose Watch。
示例应用还没有 Compose 文件。要在 TensorJS-Face-Detection
目录中创建 Compose 文件,请创建一个名为 compose.yaml
的文本文件,然后添加以下内容。
services:
server:
build:
context: .
ports:
- 80:80
develop:
watch:
- action: sync
path: .
target: /usr/share/nginx/html
此 Compose 文件定义了一个服务,该服务使用同一目录中的 Dockerfile 构建。它将主机上的 80 端口映射到容器中的 80 端口。它还有一个包含 watch
属性的 develop
子部分,该属性定义了一系列规则,用于根据本地文件更改控制服务的自动更新。有关 Compose 指令的更多详细信息,请参阅Compose 文件参考。
保存对 compose.yaml
文件的更改,然后运行以下命令来运行应用。
$ docker compose watch
应用运行后,打开 Web 浏览器访问 http://localhost:80。你可能需要授予应用访问摄像头的权限。
现在你可以修改源代码,并在容器中看到更改自动反映,无需重新构建和重新运行容器。
打开 index.js
文件,将第 83 行的面部特征点颜色从蓝色更新为绿色。
- ctx.fillStyle = "blue";
+ ctx.fillStyle = "green";
保存对 index.js
文件的更改,然后刷新浏览器页面。面部特征点现在应该显示为绿色。
要停止应用,请在终端中按 ctrl
+c
。
分享你的镜像
在 Docker Hub 上发布 Docker 镜像可以简化他人的部署流程,实现与不同项目的无缝集成。它还可以推广你的容器化解决方案,扩大其在开发者生态系统中的影响。要分享你的镜像,请执行以下操作:
注册 或登录 Docker Hub。
重新构建你的镜像,以包含对应用的更改。这次,在镜像名称前加上你的 Docker ID。Docker 使用该名称来确定要将其推送到哪个仓库。打开终端,并在
TensorJS-Face-Detection
目录中运行以下命令。将YOUR-USER-NAME
替换为你的 Docker ID。$ docker build -t YOUR-USER-NAME/face-detection-tensorjs .
运行以下
docker push
命令将镜像推送到 Docker Hub。将YOUR-USER-NAME
替换为你的 Docker ID。$ docker push YOUR-USER-NAME/face-detection-tensorjs
验证你已将镜像推送到 Docker Hub。
- 前往 Docker Hub。
- 选择 我的 Hub > 仓库。
- 查看你的仓库的 最后推送时间。
其他用户现在可以使用 docker run
命令下载并运行你的镜像。他们需要将 YOUR-USER-NAME
替换为你的 Docker ID。
$ docker run -p 80:80 YOUR-USER-NAME/face-detection-tensorjs
总结
本指南演示了如何利用 TensorFlow.js 和 Docker 在 Web 应用中实现人脸检测。它强调了运行容器化 TensorFlow.js 应用的便捷性,以及使用 Docker Compose 进行实时代码更改的开发。此外,它还介绍了如何在 Docker Hub 上分享 Docker 镜像,从而简化他人的部署,扩大应用在开发者社区中的影响力。
相关信息