You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将YOLOv8s的best.pt模型转换为可在Web应用运行的JS版本

将YOLOv8s模型部署到JavaScript Web应用(TensorFlow.js版)

1. 转换YOLOv8模型为TensorFlow.js格式

先把你训练好的best.pt模型转成TF.js兼容的格式,用Ultralytics官方工具即可:

  • 安装依赖:pip install ultralytics tensorflow tensorflowjs
  • 执行转换命令:yolo export model=best.pt format=tfjs
    完成后会生成一个包含model.json和分片权重文件的文件夹,这就是TF.js可直接加载的模型资源。

2. 搭建Web应用核心代码

创建一个HTML页面,整合TF.js加载、推理和结果可视化逻辑:

<!DOCTYPE html>
<html>
<head>
    <title>YOLOv8 自定义模型检测</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.14.0/dist/tf.min.js"></script>
</head>
<body>
    <input type="file" id="imageInput" accept="image/*">
    <img id="previewImg" style="max-width: 640px; margin: 20px 0;">
    <canvas id="detectionCanvas" style="max-width: 640px;"></canvas>

    <script>
        let yoloModel;
        // 替换成你的自定义类别列表
        const classNames = ["类别1", "类别2", "类别3"];

        // 加载TF.js模型
        async function loadYOLOModel() {
            yoloModel = await tf.loadGraphModel('./model/model.json');
            console.log("模型加载完成");
        }

        // 图片预处理(匹配YOLOv8输入要求:640x640,归一化到0-1)
        function preprocess(imgElement) {
            return tf.tidy(() => {
                const tensor = tf.browser.fromPixels(imgElement);
                const resized = tf.image.resizeBilinear(tensor, [640, 640]);
                return resized.div(255.0).expandDims(0);
            });
        }

        // 执行检测
        async function runDetection(imgElement) {
            if (!yoloModel) {
                alert("模型加载中,请稍候");
                return;
            }

            const inputTensor = preprocess(imgElement);
            const predictions = await yoloModel.predict(inputTensor).data();
            inputTensor.dispose();

            // 解析YOLOv8输出([batch, 8400, 85]:x,y,w,h,conf + 80类概率)
            const output = Array.from(predictions).reduce((acc, val, idx) => {
                const boxIdx = Math.floor(idx / 85);
                const propIdx = idx % 85;
                acc[boxIdx] = acc[boxIdx] || [];
                acc[boxIdx][propIdx] = val;
                return acc;
            }, []);

            // 非极大值抑制过滤重复框
            const validBoxes = applyNMS(output, 0.25, 0.45);
            drawDetectionResults(validBoxes, imgElement);
        }

        // 非极大值抑制实现
        function applyNMS(boxes, confThresh, iouThresh) {
            // 过滤低置信度框
            let validBoxes = boxes.filter(box => box[4] >= confThresh);
            // 按置信度降序排序
            validBoxes.sort((a, b) => b[4] - a[4]);

            const finalBoxes = [];
            while (validBoxes.length > 0) {
                const topBox = validBoxes.shift();
                finalBoxes.push(topBox);
                // 移除IOU超过阈值的重叠框
                validBoxes = validBoxes.filter(box => calculateIOU(topBox, box) < iouThresh);
            }
            return finalBoxes;
        }

        // 计算IOU(交并比)
        function calculateIOU(boxA, boxB) {
            const [x1A, y1A, wA, hA] = boxA;
            const [x1B, y1B, wB, hB] = boxB;
            const x2A = x1A + wA;
            const y2A = y1A + hA;
            const x2B = x1B + wB;
            const y2B = y1B + hB;

            const intersectX1 = Math.max(x1A, x1B);
            const intersectY1 = Math.max(y1A, y1B);
            const intersectX2 = Math.min(x2A, x2B);
            const intersectY2 = Math.min(y2A, y2B);

            const intersectArea = Math.max(0, intersectX2 - intersectX1) * Math.max(0, intersectY2 - intersectY1);
            const boxAArea = wA * hA;
            const boxBArea = wB * hB;

            return intersectArea / (boxAArea + boxBArea - intersectArea);
        }

        // 绘制检测框和标签
        function drawDetectionResults(boxes, imgElement) {
            const canvas = document.getElementById('detectionCanvas');
            const ctx = canvas.getContext('2d');
            canvas.width = imgElement.width;
            canvas.height = imgElement.height;
            ctx.drawImage(imgElement, 0, 0);

            boxes.forEach(box => {
                const [x, y, w, h, conf, ...classProbs] = box;
                const classIdx = classProbs.indexOf(Math.max(...classProbs));
                const className = classNames[classIdx];

                // 将模型输出的640x640坐标映射回原图尺寸
                const scaleX = imgElement.width / 640;
                const scaleY = imgElement.height / 640;
                const realX = x * scaleX;
                const realY = y * scaleY;
                const realW = w * scaleX;
                const realH = h * scaleY;

                // 绘制检测框
                ctx.strokeStyle = '#ff3300';
                ctx.lineWidth = 2;
                ctx.strokeRect(realX, realY, realW, realH);

                // 绘制标签背景
                ctx.fillStyle = '#ff3300';
                const label = `${className} ${conf.toFixed(2)}`;
                const labelWidth = ctx.measureText(label).width + 12;
                ctx.fillRect(realX, realY - 22, labelWidth, 20);

                // 绘制标签文字
                ctx.fillStyle = '#ffffff';
                ctx.font = '14px Arial';
                ctx.fillText(label, realX + 6, realY - 6);
            });
        }

        // 监听图片上传事件
        document.getElementById('imageInput').addEventListener('change', e => {
            const file = e.target.files[0];
            if (!file) return;
            const reader = new FileReader();
            reader.onload = evt => {
                const img = document.getElementById('previewImg');
                img.src = evt.target.result;
                img.onload = () => runDetection(img);
            };
            reader.readAsDataURL(file);
        });

        // 页面加载时启动模型加载
        window.onload = loadYOLOModel;
    </script>
</body>
</html>

3. 部署与测试

  • 将转换后的TF.js模型文件夹(包含model.json和权重文件)和上述HTML文件放在同一目录下
  • 启动本地服务器(浏览器禁止直接读取本地文件),比如用Python:python -m http.server 8000
  • 访问http://localhost:8000,上传图片即可看到检测结果

关键注意事项

  • 替换代码中的classNames数组为你自定义数据集的类别名称
  • 可根据需求调整applyNMS函数中的置信度阈值(0.25)和IOU阈值(0.45)
  • 如果模型体积过大,转换时可添加量化参数减小体积:yolo export model=best.pt format=tfjs int8

内容的提问来源于stack exchange,提问作者roee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 02:56:16