Ai-Multi-Detect

AI智能标注与检测平台

AI智能标注/训练/检测平台
账号信息
退出登录

账号信息

用户名: admin

标注工具

操作流程:
1 将图片放入 backend_data/images/ → 2 点击 扫描(输入框留空即使用默认目录) → 3 点击图片卡片进入标注 → 4 绘制多边形 · Ctrl+S 保存 → 5 点击 验证 检查数据质量 → 6 点击 准备数据集(80/20 划分) → 7 切换到 训练 标签页训练模型

验证报告

100%
加载中...

工具

操作提示

  • 点击: 添加顶点
  • 双击: 闭合多边形
  • 拖拽顶点: 移动位置
  • Ctrl+Z: 撤销
  • Del: 删除选中
  • 滚轮: 缩放
  • 空格+拖拽: 平移

多边形列表

管理标签

-
训练样本
-
类别数
-
物品模型
-
标记模型
-
GPU

训练配置

Python: 检测中... GPU: 检测中...
训练中
-
Loss
-
准确率
-
mAP50
训练日志▼
尚未开始训练
选择训练模式后点击「开始训练」,日志将在此显示

安装训练环境

首次训练需要安装 Python 和 PyTorch(需联网,约 15 分钟)。

推理测试

↳
拖拽图片到这里,或点击选择
检测记录 0 ▼
上传图片开始检测
支持拖拽上传,支持 JPG / PNG 格式
100%
裁剪物品

API 接口文档 & 发布

接口文档 拍照推理页面 cURL 示例 JS 示例

📡 推理接口

服务地址: http://localhost:3001

端点POST /api/detect/json
Content-Typeapplication/json
imageString, base64编码图片 (可含 data:image 前缀)
confidenceFloat (可选, 默认0.35), 置信度阈值

响应字段

success是否成功
detections检测对象列表: label, confidence, bbox
annotated_image标注后全图 (base64 JPEG)
cropped_items裁剪物品列表: classification_label, classification_confidence, item_confidence, item_image, annotated_item_image
saved_crops服务端保存的文件名
processing_time_ms推理耗时(毫秒)
annotated_file全图标注文件路径

📋 cURL 调用示例

curl -X POST http://localhost:3001/api/detect/json \
  -H "Content-Type: application/json" \
  -d '{
    "image": "data:image/jpeg;base64,/9j/4AAQ...",
    "confidence": 0.35
  }'

📋 JavaScript Fetch 示例

// 拍照或选择图片,获取 base64 后调用 API
async function detect(imageBase64, confidence = 0.35) {
  const r = await fetch('http://localhost:3001/api/detect/json', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ image: imageBase64, confidence })
  });
  const data = await r.json();
  if (data.success) {
    // data.annotated_image → 标注大图
    // data.cropped_items[i].item_image → 裁剪图
    // data.cropped_items[i].classification_label → "阳性"/"阴性"
    // data.cropped_items[i].classification_confidence → 置信度
    console.log(data);
  }
  return data;
}

📋 XMLHttpRequest 示例 (含上传进度)

function detectXHR(imageBase64, confidence, onProgress, onResult) {
  const xhr = new XMLHttpRequest();
  xhr.open('POST', 'http://localhost:3001/api/detect/json');
  xhr.setRequestHeader('Content-Type', 'application/json');

  // JSON 请求没有原生 upload progress,
  // 可以用 xhr.onprogress 监听响应下载进度
  xhr.onprogress = (e) => {
    if (e.lengthComputable && onProgress) {
      const pct = Math.round(e.loaded / e.total * 100);
      onProgress('下载中 ' + pct + '%');
    }
  };
  xhr.onload = () => {
    const data = JSON.parse(xhr.responseText);
    if (onResult) onResult(data);
  };
  xhr.send(JSON.stringify({ image: imageBase64, confidence }));
}

📷 独立拍照推理页面

下面是一份完整的 HTML 文件,保存为 camera.html 即可独立使用。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Multi-Detect 拍照推理</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: sans-serif; background: #1a1a2e; color: #e0e0e0; min-height: 100vh; padding: 20px; }
h1 { color: #e94560; margin-bottom: 16px; }
.controls { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
button, input { padding: 10px 20px; border: 1px solid #0f3460; border-radius: 6px; background: #16213e; color: #e0e0e0; cursor: pointer; font-size: 14px; }
button:hover { border-color: #e94560; }
video { width: 100%; max-width: 480px; border-radius: 8px; display: none; border: 1px solid #0f3460; }
canvas { display: none; }
#result { margin-top: 16px; }
#result img { max-width: 100%; border-radius: 8px; border: 1px solid #0f3460; }
.crop-card { display: inline-block; margin: 8px; background: #16213e; border-radius: 8px; overflow: hidden; border: 1px solid #0f3460; vertical-align: top; }
.crop-card img { width: 200px; height: 160px; object-fit: contain; display: block; background: #0d1120; }
.crop-card .info { padding: 8px; font-size: 13px; }
.crop-card .label { color: #e94560; font-weight: 600; }
.crop-card .conf { color: #2ecc71; }
.status { color: #a0a0c0; font-size: 13px; margin: 8px 0; }
</style>
</head>
<body>
<h1>Multi-Detect 拍照推理</h1>

<div class="controls">
  <button id="btn-camera">打开摄像头</button>
  <button id="btn-capture" disabled>拍照检测</button>
  <input type="file" id="file-input" accept="image/*">
  <label>置信度: <input id="conf" type="range" min="0.05" max="0.95" step="0.05" value="0.35">
  <span id="conf-val">0.35</span></label>
</div>

<video id="video" autoplay playsinline></video>
<canvas id="canvas"></canvas>
<div class="status" id="status"></div>
<div id="result"></div>

<script>
const API = 'http://localhost:3001/api/detect/json';
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const status = document.getElementById('status');

document.getElementById('conf').addEventListener('input', e => {
  document.getElementById('conf-val').textContent = parseFloat(e.target.value).toFixed(2);
});

document.getElementById('btn-camera').addEventListener('click', async () => {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
    video.srcObject = stream;
    video.style.display = 'block';
    document.getElementById('btn-capture').disabled = false;
  } catch (e) { status.textContent = '摄像头不可用: ' + e.message; }
});

document.getElementById('btn-capture').addEventListener('click', () => {
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  ctx.drawImage(video, 0, 0);
  const b64 = canvas.toDataURL('image/jpeg', 0.85);
  detectWith(b64);
});

document.getElementById('file-input').addEventListener('change', () => {
  const file = document.getElementById('file-input').files[0];
  if (!file) return;
  const reader = new FileReader();
  reader.onload = () => detectWith(reader.result);
  reader.readAsDataURL(file);
});

async function detectWith(imageBase64) {
  status.textContent = '检测中...';
  document.getElementById('result').innerHTML = '';
  const conf = parseFloat(document.getElementById('conf').value);
  try {
    const r = await fetch(API, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ image: imageBase64, confidence: conf })
    });
    const data = await r.json();
    if (data.success) {
      status.textContent = '检测完成 · ' + data.processing_time_ms + 'ms';
      let html = data.annotated_image
        ? '<img src="' + data.annotated_image + '" alt="检测结果">'
        : '';
      if (data.cropped_items) {
        html += data.cropped_items.map(c => {
          const pct = c.classification_confidence > 0
            ? ' ' + (c.classification_confidence * 100).toFixed(1) + '%' : '';
          const src = c.annotated_item_image || c.item_image;
          return '<div class="crop-card"><img src="' + src + '"><div class="info"><span class="label">' + (c.classification_label || '物品') + '</span><span class="conf">' + pct + '</span></div></div>';
        }).join('');
      }
      document.getElementById('result').innerHTML = html;
    } else {
      status.textContent = '检测失败: ' + (data.error || '未知');
    }
  } catch (e) { status.textContent = '网络错误: ' + e.message; }
}
</script>
</body>
</html>

使用帮助

1、标注工具 2、模型训练 3、推理检测 4、快捷键 5、常见问题

📝 标注工具

操作流程

  1. 将待标注图片放入 backend_data/images/ 目录
  2. 点击工具栏 「扫描」 按钮加载图片列表
  3. 点击图片卡片进入标注编辑器
  4. 在图片上点击添加顶点 → 双击闭合多边形 → 选择标签
  5. 每个物品需标注两个多边形:物品(测试条整体边框)和 阳性/阴性(标记区域)
  6. 按 Ctrl+S 保存当前标注

标注要点

  • 「物品」多边形应完整覆盖测试条,边缘留少量余量
  • 「阳性/阴性」多边形应精确框选标记区域(T线/C线位置)
  • 多边形顶点 ≥ 3 个,支持拖拽调整顶点位置
  • 右侧面板可查看/删除已绘制的多边形

数据准备

  1. 标注完成后点击 「验证」 检查标注完整性
  2. 验证通过后点击 「准备数据集」 自动 80/20 划分训练/验证集
  3. 数据集生成到 backend_data/datasets/ 目录

⚡ 模型训练

首次使用

切换到训练标签页,系统会自动检测训练环境。如提示「Python 未安装」,点击「安装环境」按钮,系统将自动在线安装所需依赖(需联网,约 15 分钟)。

两种训练模式

物品检测 (模型)
训练 YOLO 模型,在整张图片中定位测试条位置。使用 4 角度旋转增强数据(368 张)。
标记检测 (模型)
训练 YOLO 模型,在物品裁剪图中识别阳性/阴性标记区域。同样使用 4 角度旋转增强。

训练前准备

  1. 首次训练物品模型前,运行 rotate_data.bat 生成旋转增强数据
  2. 训练标记模型前,运行 prepare_marking.bat 裁剪物品生成标记数据集

训练参数说明

轮数训练迭代次数,推荐 500-1000
批次每批处理的图片数,显存不足时降低(如 8 或 4)
学习率模型更新步长,默认 0.0005 适用于大多数场景
图片尺寸输入分辨率,推荐 640,大尺寸需更多显存

训练监控

训练过程中实时显示 Loss(损失值)和 mAP(平均精度)。Loss 持续下降、mAP 持续上升说明训练正常。

🔍 推理检测

使用方式

  1. 切换到推理标签页
  2. 拖拽图片到上传区域,或点击选择图片文件
  3. 调整置信度阈值(默认 0.35):
    • 高召回(0.15):尽可能多检测,可能误检
    • 默认(0.35):平衡精度和召回
    • 高精度(0.55):只显示高置信度结果
  4. 点击「开始检测」

检测结果

  • 标注大图显示所有物品和标记的检测框
  • 每个物品以卡片形式展示:裁剪图 + 阴阳性分类 + 置信度
  • 点击卡片可放大查看裁剪细节(支持缩放拖拽)
  • 检测结果自动保存到 backend_data/crops/ 目录

置信度说明

卡片上显示两个数值:左侧为物品检出置信度,色彩条为阴阳性分类置信度。绿色≥70%、黄色 40-70%、红色<40%。滑块只影响分类结果的显示门槛,不影响模型内部计算。

⌨️ 快捷键

Ctrl+S保存当前标注
Ctrl+Z撤销上一个操作
Delete删除选中的多边形
鼠标滚轮缩放画布 / 灯箱图片放大缩小
空格+拖拽平移画布
双击图片灯箱中显示或闭合多边形
Esc关闭弹窗 / 灯箱

❓ 常见问题

训练到一半 GPU 没了,变成 CPU 了怎么办?
Windows TDR 超时导致 GPU 复位。以管理员身份运行一次 train_yolo26.bat(脚本已内置 TDR 修复)。
物品模型检测不到东西?
降低置信度阈值到 0.15(高召回),确认图片角度与训练数据一致。如测试图为横拍需确保已使用旋转增强数据训练。
提示「config.json 缺少 python_path」怎么办?
运行 setup_training.bat 重新安装训练环境,或在 config.json 中手动配置 Python 路径。
训练数据不足提示?
至少需要 10 个标注样本、≥2 个类别、每类 ≥5 个样本。在标注页面点击「验证」查看具体数据统计。
如何切换亮色/暗色主题?
点击标签栏右上角的太阳/月亮图标即可切换,浏览器会记住您的选择。
如何修改登录密码?
登录后点击右上角用户头像 →「账号信息」→ 输入新旧密码并保存。密码加密存储在本地数据库中。