Skip to main content
Glama
tilikumotp

Qwen-GroundingDINO-Visual-MCP

by tilikumotp
README.md
# qwen-groundingdino-visual-mcp

> **LM Studio + Qwen VL + GroundingDINO** — üretim kalitesinde açık-kelime dağarcıklı nesne tespiti ve zekice yerleştirme öneri motoru, MCP protokolü üzerinden.

---

## Mimari Özet

```
  ┌──────────────────────┐    stdio    ┌──────────────────────────────────────┐
  │  LM Studio           │◄───────────►│  annotator_mcp.py                    │
  │  Qwen VL (orkestratör)│            │  GroundingDINO (tespit motoru)        │
  │  function-calling UI │            │  Kural motoru (yerleştirme)           │
  └──────────────────────┘            │  Oturum belleği (session_memory.json) │
                                       └──────────────────────────────────────┘
```

| Bileşen | Teknoloji | Notlar |
|---|---|---|
| Orkestratör LLM | Qwen VL (LM Studio'da) | Multimodal; ne zaman hangi tool'u çağıracağına kendisi karar verir |
| Tespit motoru | `IDEA-Research/grounding-dino-base` | `transformers` üzerinden, **trust_remote_code=False** |
| Transport | stdio | HTTP/ağ bağlantısı **yoktur** |
| Model format | safetensors | GGUF/llama.cpp ile **hiçbir ilgisi yoktur** |

---

## Kurulum

### 1. Python sanal ortamı oluştur

```bash
python -m venv .venv

# Windows
.venv\Scripts\activate

# Linux/macOS
source .venv/bin/activate
```

### 2. Bağımlılıkları yükle

**CPU-only (varsayılan):**
```bash
pip install -r requirements.txt
```

**CUDA 12.1 (GPU hızlandırma):**
```bash
pip install torch==2.4.1+cu121 torchvision==0.19.1+cu121 \
    --index-url https://download.pytorch.org/whl/cu121

# Geri kalanı (torch'u tekrar yüklememek için --no-deps kullanma, sadece requirements'i yükle)
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
# ardından yukarıdaki diğer paketleri yükle
```

### 3. Sözdizimi doğrulaması

```bash
python -m py_compile annotator_mcp.py && echo "OK — sözdizimi hatası yok"
```

### 4. Model ön-indirme (isteğe bağlı)

İlk tool çağrısında model otomatik indirilir. İnternetsiz ortamlar için önden indirebilirsiniz:

```bash
python -c "
from transformers import AutoProcessor, AutoModelForZeroShotObjectDetection
AutoProcessor.from_pretrained('IDEA-Research/grounding-dino-base')
AutoModelForZeroShotObjectDetection.from_pretrained('IDEA-Research/grounding-dino-base')
print('Model önbelleğe alındı.')
"
```

---

## LM Studio Yapılandırması — `mcp.json`

LM Studio'nun MCP ayarlar dosyasına aşağıdaki bloğu ekleyin:

```json
{
  "mcpServers": {
    "groundingdino-annotator": {
      "command": "python",
      "args": [
        "C:/Users/ASUS/Projects/qwen-groundingdino-visual-mcp/annotator_mcp.py"
      ],
      "env": {
        "GROUNDING_DINO_MODEL_ID": "IDEA-Research/grounding-dino-base",
        "BOX_THRESHOLD": "0.35",
        "TEXT_THRESHOLD": "0.25"
      }
    }
  }
}
```

> **Not:** `command` için sanal ortamdaki Python yolunu kullanmak daha güvenlidir:
> `"C:/Users/ASUS/Projects/qwen-groundingdino-visual-mcp/.venv/Scripts/python.exe"`

### Hafif model (daha az bellek):
```json
"GROUNDING_DINO_MODEL_ID": "IDEA-Research/grounding-dino-tiny"
```

---

## Örnek Sohbet Akışı

### Senaryo: "Masadaki vazoyu bul, ardından yanına bir kupa öner"

```
Kullanıcı:
  C:/sahne/mutfak.jpg görüntüsündeki masayı ve vazoyu bul.

Qwen → detect_objects({
  "image_path": "C:/sahne/mutfak.jpg",
  "query": "masa, vazo",
  "session_id": "mutfak_sahnesi"
})

Sunucu yanıtı:
{
  "session_id": "mutfak_sahnesi",
  "count": 2,
  "detections": [
    { "label": "masa",  "score": 0.82, "bbox": [120, 450, 880, 950] },
    { "label": "vazo",  "score": 0.74, "bbox": [400, 200, 600, 450] }
  ]
}

---

Kullanıcı:
  Vazonun sağına ideal kupa yerleştirme noktasını öner.

Qwen → suggest_ideal_placements({
  "session_id": "mutfak_sahnesi",
  "target_object": "kupa",
  "reference_object": "vazo",
  "relation": "right_of"
})

Sunucu yanıtı:
{
  "suggested_bbox": [610, 250, 760, 440],
  "score": 0.8712,
  "rule_breakdown": {
    "overlap_penalty": 1.0,
    "edge_margin": 0.82,
    "spacing_from_objects": 0.61,
    "prefer_center": 0.73
  }
}

---

Kullanıcı:
  Bu koordinatları görüntüye çiz ve kaydet.

Qwen → annotate_image({
  "image_path": "C:/sahne/mutfak.jpg",
  "output_path": "C:/sahne/mutfak_annotated.jpg",
  "session_id": "mutfak_sahnesi",
  "annotations": [
    { "label": "masa",  "bbox": [120, 450, 880, 950] },
    { "label": "vazo",  "bbox": [400, 200, 600, 450] },
    { "label": "kupa",  "bbox": [610, 250, 760, 440] }
  ]
})

Sonuç: 3 nesne kırmızı çerçeve + etiketle işaretlenmiş görüntü kaydedildi.
```

---

## Koordinat Sistemi

Tüm bbox koordinatları **0-1000 normalize skalasında** `[x1, y1, x2, y2]` formatındadır:

```
(0,0) ─────────────── (1000,0)
  │                        │
  │     görüntü alanı      │
  │                        │
(0,1000) ───────────(1000,1000)
```

Bu skala Qwen VL'nin koordinat çıktısıyla tutarlıdır.

---

## Tool Referansı

| Tool | Amaç | Zorunlu Parametreler |
|------|-------|----------------------|
| `detect_objects` | GroundingDINO tespiti | `image_path`, `query` |
| `register_detected_objects` | Manuel nesne kaydı | `session_id`, `objects` |
| `annotate_image` | Görüntü üzerine çizim | `image_path`, `output_path`, `annotations` |
| `suggest_ideal_placements` | Yerleştirme önerisi | `session_id`, `target_object` |
| `list_session_objects` | Oturum durumu | `session_id` |
| `reset_session` | Bellek temizleme | `session_id` |

---

## Doğrulama Testi

Aşağıdaki Python betiği, MCP olmadan doğrudan sunucunun tespit fonksiyonunu test eder:

```python
# test_detect.py
import sys
sys.path.insert(0, ".")

from annotator_mcp import format_groundingdino_query, _run_detection_sync

# 1. Sorgu formatlama testi
q = format_groundingdino_query("Masa, VAZO, Kapı")
assert q == "masa . vazo . kapı .", f"Beklenen 'masa . vazo . kapı .' ama '{q}' geldi"
print(f"✓ format_groundingdino_query: '{q}'")

# 2. Gerçek tespit testi (bir görüntü gerektirir)
TEST_IMAGE = "test.jpg"   # Var olan bir .jpg yolu girin

import os
if os.path.isfile(TEST_IMAGE):
    results = _run_detection_sync(
        image_path=TEST_IMAGE,
        query="insan, masa, sandalye",
        box_threshold=0.35,
        text_threshold=0.25,
        session_id="test_session",
    )
    print(f"✓ Tespit tamamlandı: {len(results)} nesne bulundu")
    for r in results:
        print(f"   [{r['label']:15s}] skor={r['score']:.3f}  bbox={r['bbox']}")
else:
    print(f"⚠ {TEST_IMAGE} bulunamadı — tespit adımı atlandı")

print("\n✓ Tüm birim testler geçti.")
```

Çalıştırma:
```bash
python test_detect.py
```

Beklenen çıktı formatı:
```json
{
  "session_id": "test_session",
  "count": 2,
  "detections": [
    { "label": "masa",    "score": 0.7812, "bbox": [45, 380, 955, 920] },
    { "label": "sandalye","score": 0.6531, "bbox": [20, 420, 300, 890] }
  ]
}
```

---

## ⚠️ Bilinen Tuzaklar

> Bu bölüm, proje geliştirme sırasında gerçekten yaşanan sorunları belgeler.
> Gelecekteki geliştiriciler ve AI sistemleri için rehber niteliğindedir.

### 1. GGUF vs safetensors karışıklığı

**Sorun:** GroundingDINO'yu LM Studio'nun GGUF/llama.cpp arayüzüyle yüklemeye çalışmak.

**Belirti:** `gguf_init_from_reader: tensor name too long` veya benzeri anlamsız hatalar.

**Neden:** GroundingDINO (ve genel olarak tüm `trust_remote_code`/özel mimari modeller) llama.cpp ekosistemiyle **uyumlu değildir**. Bu model safetensors formatındadır ve GGUF'a dönüştürülemez.

**Çözüm:** Model **yalnızca** bu Python süreci içinde `transformers.from_pretrained()` ile, orijinal safetensors formatında yönetilmelidir. LM Studio'nun model arayüzüne **hiç dokunmayın**.

---

### 2. transformers sürüm uyumsuzluğu

**Sorun:** `requirements.txt`'te `transformers` sürümü pinlenmemişse en güncel sürüm kurulabilir ve API kırılabilir.

**Belirti:** `TypeError: post_process_grounded_object_detection() got unexpected keyword argument 'input_ids'` veya tam tersi.

**Neden:** `post_process_grounded_object_detection`'ın fonksiyon imzası transformers sürümleri arasında değişmiştir.

**Çözüm (bu projede uygulanmıştır):** `requirements.txt`'te `transformers==4.44.2` olarak pinlendi. Ek olarak kod, yeni imzayı dener; `TypeError` alırsa eski imzaya otomatik düşer (try/except cascade). Yine de sürümü değiştirmeden önce mutlaka test edin.

---

### 3. Event loop bloklanması

**Sorun:** `_run_detection_sync()` veya `_find_best_placement_sync()` async `call_tool` handler'ından `asyncio.to_thread` olmadan doğrudan çağrılırsa.

**Belirti:** Inference süresince (1-5 saniye) MCP sunucusu tüm diğer isteklere (ping, iptal) kilitlenir.

**Çözüm (bu projede uygulanmıştır):** Tüm CPU/GPU-bound fonksiyonlar `await asyncio.to_thread(...)` ile sarılmıştır. Hiçbir zaman bu sarımı kaldırmayın.

---

### 4. Hayali dosya yolu (Hallucinated image_path)

**Sorun:** LLM, sohbete sürükle-bırak yapılan bir görseli "gördüğü" için (base64 olarak) bazen bu görselin disk yolunu uydurabilir.

**Belirti:** `FileNotFoundError: 'C:/imagined/path.jpg'`

**Neden:** Model görseli base64 olarak görüyor; gerçek disk yolunu bilmiyor.

**Çözüm:** 
- Tool description'larında açıkça belirtilmiştir: `image_path` gerçek bir disk yolu olmalıdır.
- Kod, `Image.open()` başarısız olursa net hata döndürür: *"Dosya bulunamadı: ... Lütfen diskte gerçekten var olan bir dosya yolu belirtin."*
- Kullanıcıların her zaman gerçek dosya yolunu metin olarak yazması gerekir.

---

### 5. MCP handshake timeout

**Sorun:** Model `import` anında veya `annotator_mcp.py` başlangıcında yüklenirse.

**Belirti:** LM Studio, MCP sunucusunu başlatır ama ~3 saniye içinde handshake yanıtı alamaz ve bağlantıyı düşürür. Log: `MCP server connection timed out` veya benzeri.

**Neden:** Model yükleme (1-2 GB safetensors, birkaç saniye) handshake penceresini aşıyor.

**Çözüm (bu projede uygulanmıştır):** Lazy loading zorunludur. `_model = None` ile başlar; `load_model()` yalnızca **ilk tool çağrısında** tetiklenir. `torch` ve `transformers` import'ları da `load_model()` içinde yapılır (yavaş import'ları geciktirmek için).

---

### 6. Florence-2 ile karıştırma

**Sorun:** Florence-2 gibi `trust_remote_code=True` gerektiren modellerin kurulum mantığını GroundingDINO'ya uygulamak.

**Fark:** GroundingDINO, transformers 4.38+ sürümünde resmi olarak entegre edilmiştir ve `trust_remote_code` gerektirmez. Bu, Florence-2'ye göre önemli bir **kararlılık avantajıdır** — remote code her güncellemede kırılabilir, resmi entegre kod kırılmaz.

**Çözüm:** `from_pretrained()` çağrılarında `trust_remote_code` parametresi hiç kullanmayın.

---

## Proje Yapısı

```
qwen-groundingdino-visual-mcp/
├── annotator_mcp.py       # Ana MCP sunucusu (tek dosya, tüm mantık burada)
├── requirements.txt       # Sabit sürümlü bağımlılıklar
├── README.md              # Bu dosya
└── session_memory.json    # Çalışma zamanında oluşturulur (otomatik)
```

---

## Lisans

MIT — Dilediğiniz gibi kullanın, değiştirin ve dağıtın.