内容
-groundingdino-visual-mcp
> **LM Studio + Qwen VL + GroundingDINO** — 生产质量的开放词汇物体检测和智能放置推荐引擎,通过MCP协议。
---
## 架构概述
```
┌──────────────────────┐ stdio ┌──────────────────────────────────────┐
│ LM Studio │◄►│ annotator_mcp.py │
Qwen VL (编排器)│ │ GroundingDINO (检测引擎) │
│ function-calling UI │ │规则引擎 (放置) └────────────────────── 会话内存_memory.json) │
└──────────────────────────────────────┘
```
| 组件 | 技术 | 备注 |
|---|---|---|
| 编排LLM | Qwen VLLM Studio中) | 多模态;根据需要自动调用工具 |
| 检测引擎 | `IDEA-Research/grounding-dino-base` | 通过 `transformers`,**trust_remote_code=False** |
| 传输 | stdio | **无** HTTP/网络连接 |
| 模型格式 | safetensors | 与 GGUF/llama.cpp **无关** |
---
## 安装
### 1. 创建Python虚拟环境
```bash
python -m venv .venv
# Windows
.venv\Scripts\activate
# Linux/macOS
source .venv/bin/activate
```
### 2. 安装依赖
**仅CPU(默认):**
```bash
pip install -r requirements.txt
```
**CUDA 1(GPU加速):
pip install torch.4.1+cu==0.19+cu121 \
index-url https://download.org/whl/cu121
# 剩余部分(不要重新安装torch,使用 --no-deps,只安装requirements)
pip install transformers==4.44.2 accelerate==0.34.2 huggingface_hub==0.25.1 \
Pillow==10.4.0 numpy==1.26.4 mcp==1.3.0 tokenizers==0.19.1
```
**CUDA 11.8:**
```bash
pip install torch==2.4.1+cu118 torchvision==0.19.1+cu118 \
--index-url https://download.pytorch.org/whl/cu118
# 然后安装
```
###. 语法验证
python -m annotator_mcp.py "OK — 无语法```
### 4. 下载(可选)
首次调用工具时会自动。对于无互联网环境,可以下载:
```python -c "
from AutoProcessor, AutoModelForZeroShotObjectDetection
AutoProcessor.from_pretrained('IDEA-Research/grounding-dino-base')
AutoModelForZeroShotObjectDetection.from_pretrained('IDEA-Research/grounding-dino-base')
print('模型已缓存。')
"
```
---
## LM Studio 配置 — `m`
将以下块 Studio的MCP设置
```json
{
mcpServers": {
"groundingdinoator": {
"command": "python",
" [
"C/ASUS/Projects/qwendino-visual-mcp/annotator_mcp.py "env": "GROUNDING_DINO_MODEL_ID": "IDEA/grounding-dino-base",
"BOX_THRESHOLD0.35",
_THRESHOLD": "0.25"
}
}
}
}
```
> **注意:** 对于 `command`,使用虚拟环境中的Python路径更安全:
> `"C:/US/Projects/qwen-groundingdino-visual-mcp/.venv/Scripts/python.exe"`
### 轻量级模型(更少的内存):
```json
"GROUNDING_DINO_MODEL_ID": "IDEA-Research/grounding-dino-tiny"
```
---
## 示例对话流程
### 场景: "找到桌子上的花瓶,然后建议在它旁边放置一个杯子"
```
用户:
在图像 C:/sahne/mutfak.jpg 中找到桌子和花瓶。
Qwen → detect_objects({
"image_path": "C:/sahne/mutfak.jpg",
"query":子,花瓶",
"session_id": "mutfak_sahnesi"
})
服务器响应:
{
"session_id": "mutfak_sahnesi",
" 2,
etections": [
label": "桌子 "score": 0.82, "bbox": [ 450,, 950] },
{ "label": "花瓶", "score":.74, "bbox": [400, 200600, 450] }
]
}
---
用户 在花瓶右侧建议理想的杯子放置点wen → suggest_ideal_placements({
"session "mutfak_s",
"target "杯子",
"reference_object": "花 "relation": ""
})
服务器响应:
{
"suggested [610, 250, 440 "score": 0.8712,
"down": {
_penalty": 1,
"edge 0.82,
spacing_from_objects": 61,
"prefer_center": 0. }
}
---
用户 将这些坐标绘制上并保存。
Qwen_image({
"image_path": "C:/s/mutfak "output_pathC:/sahneak_annotated "session_id":fak_sahnes "annotations": [
"label": "", "bbox120, 450, 880, 950] { "label": "", "bbox": [400, 200 450] { "label":子", "bbox": [610, 250760, 440 ]
})
结果:个物体(红色 + 标签)的图像已保存。
```
---
## 坐标系统
所有边界框坐标均在 **0-1000 归一化尺度** 上,格式为 `[x1, y1, x2, y2]`:
```
(0,0) ─────────────── (1000,0)
│ │
图像 │
│ 0,1000──────────(1000,100```
该尺度与Qwen VL的坐标输出一致。
---
工具参考
| 工具 | 目的 | 必需参数 |
|------|-------|----------------------|
| `detect_objects` | GroundingDINO检测 | `image_path`, `query` |
| `register_detected_objects` | 手动物体注册 | `session_id`, `objects` |
| `annotate_image` | 在图像上绘图 | `image_path`, `output_path`, `annotations` |
| `suggest_ideal_placements` | 放置建议 | `session_id`, `target_object` |
| `list_session_objects` | 会话状态 | `session_id` |
| `reset_session` | 清除内存 | `session_id` |
---
## 验证测试
以下Python脚本在没有MCP的情况下直接测试服务器的检测功能:
```python
# test_detect.py
import sys
sys.path.insert(0, ".")
from annotator_mcp import format_groundingdino_query, _run_detection_sync
# 1. 查询格式化测试
q = format_groundingdino_query("桌子, 花瓶, 门")
assert q == "桌子 . 花瓶 . 门 .", f"预期 '桌子 . 花瓶 . 门 .' 但得到 '{q}'"
print(f"✓ format_groundingdino_query: '{q}'")
# 2. 实际检测测试(需要图像)
TEST_IMAGE = "test.jpg" # 请输入现有的.jpg文件路径
import os
if os.path.isfile(TEST_IMAGE):
results = _run_detection_sync(
image_path=TEST_IMAGE,
query="人, 桌子, 椅子",
_threshold=0.35,
text_threshold=0.25,
session_id="test_session",
)
print(f"✓ 检测完成:找到 {len(results)} 个物体")
for r in results:
print(f" [{r['label']:15s}] 得分={r['score']:.3f} 边界框={r['bbox']}")
else:
print(f"⚠ {TEST_IMAGE} 未找到 — 跳过检测步骤")
print("\n✓ 所有单元测试通过。")
```
运行:
```bash
python test_detect.py
```
预期输出格式:
```json
{
"session_id": "test_session",
"count": 2,
"detections": [
{ "label": "桌子", "score": 0.7812, "bbox": [45, 380, 955, 920] },
{ "label": "椅子", "score": 0.6531, "bbox": [20, 420, 300, 890] }
]
}
```
---
## ⚠️ 已知陷阱
> 本节记录了项目开发过程中实际遇到的问题。
> 适用于未来的开发者和AI系统。
### 1. GGUF vs safetensors 混淆
**问题:** 尝试使用LM Studio的GGUF/llama.cpp接口加载GroundingDINO。
**症状:** `gguf_init_from_reader: tensor name too long` 或类似的无意义错误。
**原因:** GroundingDINO(以及所有 `trust_remote_code`/自定义架构模型)**不兼容** llama.cpp 生态系统。 该模型采用 safetensors 格式,**无法转换为** GGUF。
**解决方案:** 模型 **仅在** 当前 Python 进程中使用 `transformers.from_pretrained()` 以原始 safetensors 格式管理。 **请勿** 触摸 LM Studio 的模型接口。
---
### 2. transformers 版本不兼容
**问题:** 如果 `requirements.txt` 中未固定 `transformers` 版本,可能会安装最新版本并破坏 API。
**症状:** `TypeError: post_process_grounded_object_detection() got unexpected keyword argument 'input_ids'` 或类似错误。
**原因:** `post_process_grounded_object_detection` 的函数签名在不同 transformers 版本之间更改。
**解决方案(已应用):** 在 `requirements.txt` 中固定为 `transformers==4.44.2`。 此外,代码尝试新的签名;如果出现 `TypeError`,则自动回退到旧签名(try/except cascade)。 在更改版本之前,请务必进行测试。
---
### 3. 事件循环阻塞
**问题:** 如果 `_run_detection_sync()` 或 `_find_best_placement_sync()` 直接从异步 `call_tool` 处理程序调用,而不使用 `asyncio.to_thread`。
**症状:** 在推理期间(1-5 秒),MCP 服务器锁定所有其他请求(ping、取消)。
**解决方案(已应用):** 所有 CPU/GPU 绑定函数都使用 `await asyncio.to_thread(...)` 包装。 **请勿** 删除此包装。
---
### 4. 虚构的文件路径(Hallucinated image_path)
**问题:** LLM 通过拖放图像到聊天中“看到”图像(以 base64 格式),有时会构造该图像的磁盘路径。
**症状:** `FileNotFoundError: 'C:/imagined/path.jpg'`
**原因:** 模型将图像视为 base64;不知道实际的磁盘路径。
**解决方案:**
- 在工具描述中明确指出:`image_path` 必须是实际的磁盘路径。
- 如果 `Image.open()` 失败,代码返回明确的错误: *"未找到文件:... 请指定一个实际存在的磁盘路径。"*
- 用户必须始终将实际文件路径作为文本写入。
---
### 5. MCP 握手超时
**问题:** 模型在 `import` 时或 `annotator_mcp.py` 启动时加载。
**症状:** LM Studio 启动 MCP 服务器,但在 ~3 秒内未收到握手响应并断开连接。 日志:`MCP server connection timed out` 或类似。
**原因:** 模型加载(1-2 GB safetensors,几秒钟)超过握手窗口。
**解决方案(已应用):** 延迟加载是必须的。 `_model = None` 开始;`load_model()` 仅在 **第一个工具调用** 时触发。 `torch` 和 `transformers` 导入也在 `load_model()` 中完成(延迟慢速导入)。
---
### 6. 与 Florence-2 混淆
**问题:** 将需要 `trust_remote_code=True` 的模型(如 Florence-2)的安装逻辑应用于 GroundingDINO。
**区别:** GroundingDINO 在 transformers 4.38+ 版本中正式集成,**不要求** `trust_remote_code`。 这是相对于 Florence-2 的 **显著稳定性优势** —— 远程代码每次更新都可能中断,集成的代码不会中断。
**解决方案:**from_pretrained()` 调用中 **不要** 使用 `trust_remote_code` 参数。
---
## 项目结构
```
qwen-groundingdino-visual-mcp/
├── annotator_mcp.py # 主要的MCP服务器(单个文件,所有逻辑都在这里)
├── requirements.txt # 具有固定版本的依赖项
├── README.md # 此文件
└── session_memory.json # 运行时创建(自动)
```
---
## 许可证
MIT — 您可以自由使用、修改和分发。