如何将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
相关产品推荐
相关产品推荐

