Transformers.js 文档
构建 Vanilla JavaScript 应用
并获得增强的文档体验
开始使用
构建 Vanilla JavaScript 应用
在本教程中,你将使用 Transformers.js 构建一个简单的 Web 应用来检测图像中的物体!要跟着做,你只需要一个代码编辑器、一个浏览器和一个简单的服务器(例如,VS Code Live Server)。
它的工作原理是:用户点击“上传图片”,然后使用输入对话框选择一张图片。在使用目标检测模型分析图片后,预测的边界框将叠加在图片上,就像这样
实用链接
第 1 步:HTML 和 CSS 设置
在我们开始使用 Transformers.js 构建之前,我们首先需要用一些标记和样式打下基础。创建一个名为 index.html
的文件,包含基本的 HTML 骨架,并将以下 <main>
标签添加到 <body>
中
<main class="container">
<label class="custom-file-upload">
<input id="file-upload" type="file" accept="image/*" />
<img class="upload-icon" src="https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/upload-icon.png" />
Upload image
</label>
<div id="image-container"></div>
<p id="status"></p>
</main>
点击此处查看此标记的分解说明。
我们正在添加一个 <input>
元素,其 type="file"
用于接受图片。这允许用户通过弹出对话框从本地文件系统选择图片。该元素的默认样式看起来很糟糕,所以我们来添加一些样式。最简单的方法是将 <input>
元素包裹在 <label>
中,隐藏输入框,然后将标签样式化为按钮。
我们还添加了一个空的 <div>
容器来显示图片,外加一个空的 <p>
标签,我们将用它来向用户提供状态更新,因为下载和运行模型都需要一些时间。
接下来,在一个 style.css
文件中添加以下 CSS 规则,并将其链接到 HTML 中
html,
body {
font-family: Arial, Helvetica, sans-serif;
}
.container {
margin: 40px auto;
width: max(50vw, 400px);
display: flex;
flex-direction: column;
align-items: center;
}
.custom-file-upload {
display: flex;
align-items: center;
gap: 10px;
border: 2px solid black;
padding: 8px 16px;
cursor: pointer;
border-radius: 6px;
}
#file-upload {
display: none;
}
.upload-icon {
width: 30px;
}
#image-container {
width: 100%;
margin-top: 20px;
position: relative;
}
#image-container>img {
width: 100%;
}
这是此时 UI 的样子
第 2 步:JavaScript 设置
无聊的 部分完成了,现在开始写一些 JavaScript 代码吧!创建一个名为 index.js
的文件,并在 index.html
中链接它,方法是在 <body>
的末尾添加以下内容
<script src="./index.js" type="module"></script>
type="module"
属性很重要,因为它将我们的文件变成一个JavaScript 模块,这意味着我们将能够使用导入和导出。
转到 index.js
,让我们通过在文件顶部添加以下行来导入 Transformers.js
import { pipeline, env } from "https://cdn.jsdelivr.net.cn/npm/@huggingface/transformers";
由于我们将从 Hugging Face Hub 下载模型,我们可以通过设置以下内容来跳过本地模型检查
env.allowLocalModels = false;
接下来,让我们创建对稍后将访问的各种 DOM 元素的引用
const fileUpload = document.getElementById("file-upload");
const imageContainer = document.getElementById("image-container");
const status = document.getElementById("status");
第 3 步:创建一个目标检测 pipeline
我们终于准备好创建我们的目标检测 pipeline 了!提醒一下,pipeline 是该库提供的一个高级接口,用于执行特定任务。在我们的例子中,我们将使用 pipeline()
辅助函数来实例化一个目标检测 pipeline。
由于这可能需要一些时间(尤其是第一次下载约 40MB 的模型时),我们首先更新 status
段落,以便用户知道我们即将加载模型。
status.textContent = "Loading model...";
为了使本教程简单,我们将在主 (UI) 线程中加载和运行模型。这不推荐用于生产应用,因为当我们执行这些操作时,UI 会冻结。这是因为 JavaScript 是一种单线程语言。为了解决这个问题,你可以使用 web worker 在后台下载和运行模型。但是,我们不会在本教程中涵盖这一点……
我们现在可以调用我们在文件顶部导入的 pipeline()
函数,来创建我们的目标检测 pipeline
const detector = await pipeline("object-detection", "Xenova/detr-resnet-50");
我们向 pipeline()
函数传递了两个参数:(1) task 和 (2) model。
第一个参数告诉 Transformers.js 我们想执行什么样的任务。在我们的例子中,是
object-detection
,但该库支持许多其他任务,包括text-generation
、sentiment-analysis
、summarization
或automatic-speech-recognition
。请参阅此处获取完整列表。第二个参数指定了我们希望使用哪个模型来解决给定的任务。我们将使用
Xenova/detr-resnet-50
,因为它是一个相对较小(约 40MB)但功能强大的模型,用于检测图像中的物体。
函数返回后,我们会告诉用户应用已准备就绪。
status.textContent = "Ready";
第 4 步:创建图片上传器
下一步是支持上传/选择图片。为此,我们将监听 fileUpload
元素的“change”事件。在回调函数中,如果选择了图片(否则不执行任何操作),我们使用 FileReader()
来读取图片的内容。
fileUpload.addEventListener("change", function (e) {
const file = e.target.files[0];
if (!file) {
return;
}
const reader = new FileReader();
// Set up a callback when the file is loaded
reader.onload = function (e2) {
imageContainer.innerHTML = "";
const image = document.createElement("img");
image.src = e2.target.result;
imageContainer.appendChild(image);
// detect(image); // Uncomment this line to run the model
};
reader.readAsDataURL(file);
});
图片加载到浏览器后,将调用 reader.onload
回调函数。在其中,我们将新的 <img>
元素附加到 imageContainer
中以显示给用户。
别担心 detect(image)
函数调用(它被注释掉了)——我们稍后会解释它!现在,尝试运行应用并上传一张图片到浏览器。你应该看到你的图片显示在按钮下方,像这样
第 5 步:运行模型
我们终于准备好开始与 Transformers.js 互动了!让我们取消上面代码片段中 detect(image)
函数调用的注释。然后我们将定义函数本身
async function detect(img) {
status.textContent = "Analysing...";
const output = await detector(img.src, {
threshold: 0.5,
percentage: true,
});
status.textContent = "";
console.log("output", output);
// ...
}
注意:detect
函数需要是异步的,因为我们将 await
模型的结果。
一旦我们将 status
更新为“正在分析”,我们就准备好执行推理,这仅仅意味着用一些数据来运行模型。这是通过 pipeline()
返回的 detector()
函数完成的。我们传递的第一个参数是图片数据 (img.src
)。
第二个参数是一个选项对象
- 我们将
threshold
属性设置为0.5
。这意味着我们希望模型在声称检测到图像中的物体之前至少有 50% 的置信度。阈值越低,它检测到的物体就越多(但可能会误识别物体);阈值越高,它检测到的物体就越少(但可能会错过场景中的物体)。 - 我们还指定了
percentage: true
,这意味着我们希望物体的边界框以百分比(而不是像素)的形式返回。
如果你现在尝试运行应用并上传一张图片,你应该会在控制台中看到以下输出
在上面的例子中,我们上传了一张有两只大象的图片,所以 output
变量包含一个有两个对象的数组,每个对象都包含一个 label
(字符串“elephant”)、一个 score
(表示模型对其预测的置信度)和一个 box
对象(表示检测到的实体的边界框)。
第 6 步:渲染边界框
最后一步是将 box
坐标显示为围绕每只大象的矩形。
在 detect()
函数的末尾,我们将对 output
数组中的每个对象运行 renderBox
函数,使用 .forEach()
。
output.forEach(renderBox);
这是 renderBox()
函数的代码,带有注释以帮助你理解发生了什么
// Render a bounding box and label on the image
function renderBox({ box, label }) {
const { xmax, xmin, ymax, ymin } = box;
// Generate a random color for the box
const color = "#" + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, 0);
// Draw the box
const boxElement = document.createElement("div");
boxElement.className = "bounding-box";
Object.assign(boxElement.style, {
borderColor: color,
left: 100 * xmin + "%",
top: 100 * ymin + "%",
width: 100 * (xmax - xmin) + "%",
height: 100 * (ymax - ymin) + "%",
});
// Draw the label
const labelElement = document.createElement("span");
labelElement.textContent = label;
labelElement.className = "bounding-box-label";
labelElement.style.backgroundColor = color;
boxElement.appendChild(labelElement);
imageContainer.appendChild(boxElement);
}
边界框和标签 span 也需要一些样式,所以将以下内容添加到 style.css
文件中
.bounding-box {
position: absolute;
box-sizing: border-box;
border-width: 2px;
border-style: solid;
}
.bounding-box-label {
color: white;
position: absolute;
font-size: 12px;
margin-top: -16px;
margin-left: -2px;
padding: 1px;
}
就这样!
你现在已经构建了自己的功能齐全的 AI 应用,它可以在图像中检测物体,并且完全在你的浏览器中运行:无需外部服务器、API 或构建工具。太酷了!🥳
该应用可在以下 URL 实时访问:https://huggingface.co/spaces/Scrimba/vanilla-js-object-detector
< > 在 GitHub 上更新