Skip to content

Latest commit

 

History

History
365 lines (282 loc) · 10.1 KB

File metadata and controls

365 lines (282 loc) · 10.1 KB

🚀 Propozycje Dalszych Ulepszeń i Optymalizacji

✅ Już Zaimplementowane

  1. ✅ Time-based gallery cleaning (timeout 30s)
  2. ✅ Hungarian algorithm dla optymalnego matchingu
  3. ✅ Stabilniejsze embeddingi (alpha=0.3)
  4. ✅ Tracking statystyk (last_seen, appearances)
  5. ✅ Threshold obniżony do 0.5

🎯 Propozycje Dalszych Ulepszeń

1. Temporal Smoothing - Filtr Kalmana

Problem: ID może migać gdy osoba jest częściowo zasłonięta lub w trudnym kącie

Rozwiązanie:

class TemporalTracker:
    def __init__(self, smoothing_window=5):
        self.id_history = {}  # {track_id: [last 5 IDs]}
        
    def smooth_id(self, bbox_center, proposed_id):
        # Znajdź najbliższy track po pozycji
        # Jeśli ID jest spójne z historią → zachowaj
        # Jeśli nowe ID → wymagaj 3/5 głosów
        pass

Efekt: Stabilniejsze ID nawet przy ocklusion


2. Spatial Constraint - IoU Filtering

Problem: Osoba przeskakuje przez ekran z ID 1 na ID 2

Rozwiązanie:

def spatial_constrained_match(prev_bboxes, curr_bboxes, prev_ids):
    # Oblicz IoU między prev i curr bboxes
    # Jeśli IoU > 0.3 → prawdopodobnie ta sama osoba
    # Użyj spatial prior w Hungarian matching
    iou_matrix = compute_iou_matrix(prev_bboxes, curr_bboxes)
    combined_score = 0.7 * similarity + 0.3 * iou

Efekt: Fizycznie bliska detekcja dostaje ten sam ID


3. Multi-Camera Fusion (dla przyszłości)

Problem: Osoba idzie z kamery 1 na kamerę 2 → nowe ID

Rozwiązanie:

class GlobalGallery:
    def __init__(self):
        self.global_embeddings = {}  # Wspólna galeria dla wszystkich kamer
        
    def match_across_cameras(self, emb, camera_id, timestamp):
        # Szukaj w global gallery
        # Uwzględnij czas i topologię kamer
        pass

Efekt: Konsystentne ID między kamerami


4. Adaptive Threshold - Na podstawie confidence

Problem: Threshold 0.5 może być za wysoki/niski w różnych warunkach

Rozwiązanie:

def adaptive_threshold(base_threshold, detection_confidence, lighting):
    # Wysoka confidence YOLO → niższy threshold (więcej ufności)
    # Niska confidence → wyższy threshold (ostrożniej)
    threshold = base_threshold * (2 - detection_confidence)
    return np.clip(threshold, 0.3, 0.7)

Efekt: Dynamiczne dostosowanie do warunków


5. GPU Acceleration

Problem: CPU daje ~7 FPS, potrzeba więcej dla realtime

Rozwiązanie:

# Batch inference dla OSNet
def extract_features_batch(images):
    batch = torch.stack([transform(img) for img in images])
    with torch.no_grad():
        features = model(batch.to('cuda'))
    return features

# Użyj CUDA streams dla parallel processing

Efekt: 30-50 FPS na GPU


6. Lightweight Model - OSNet Tiny

Problem: osnet_x1_0 jest duży (10.9MB), wolny inference

Rozwiązanie:

# Użyj osnet_x0_25 lub osnet_ain_x0_5
model = models.build_model(
    name='osnet_x0_25',  # 1/4 rozmiaru, 2x szybszy
    pretrained=True
)

Efekt: 12-15 FPS na CPU (przy minimalnej stracie accuracy)


7. Feature Caching - Dla duplikatów

Problem: Ta sama detekcja w podobnych klatkach → redundantne obliczenia

Rozwiązanie:

class FeatureCache:
    def __init__(self, max_age=5):
        self.cache = {}  # {bbox_hash: (features, timestamp)}
        
    def get_or_compute(self, bbox, image):
        bbox_hash = hash_bbox(bbox)
        if bbox_hash in cache and age < max_age:
            return cache[bbox_hash]
        features = extract_features(crop_bbox(image, bbox))
        cache[bbox_hash] = (features, time.time())
        return features

Efekt: 20-30% przyspieszenie dla statycznych osób


8. Pose-Aware ReID

Problem: Osoba w różnych pozach (stoi/siedzi) może mieć różne embeddingi

Rozwiązanie:

# Użyj pose estimation (np. MediaPipe Pose)
def extract_pose_normalized_features(image, keypoints):
    # Znormalizuj crop do standardowej pozy
    aligned = align_by_keypoints(image, keypoints)
    features = model(aligned)
    return features

Efekt: Robustness na zmiany pozy


9. Attention Mechanism - Weighted Pooling

Problem: OSNet traktuje całe ciało równo, ale głowa/torso ważniejsze

Rozwiązanie:

class AttentionOSNet(nn.Module):
    def __init__(self):
        self.osnet = osnet_x1_0()
        self.attention = nn.Sequential(
            nn.Conv2d(512, 1, 1),
            nn.Sigmoid()
        )
        
    def forward(self, x):
        features = self.osnet.conv5(x)  # (B, 512, H, W)
        attention_map = self.attention(features)  # (B, 1, H, W)
        weighted = features * attention_map
        pooled = F.adaptive_avg_pool2d(weighted, 1)
        return pooled

Efekt: Lepsze fokusowanie na istotnych regionach


10. Occlusion Handling - Part-based Matching

Problem: Zasłonięta osoba (za słupem) → trudne rozpoznanie

Rozwiązanie:

def extract_part_features(image):
    # Podziel na 3 części: głowa, torso, nogi
    h, w = image.shape[:2]
    head = image[0:h//3, :]
    torso = image[h//3:2*h//3, :]
    legs = image[2*h//3:, :]
    
    head_emb = model(head)
    torso_emb = model(torso)
    legs_emb = model(legs)
    
    return [head_emb, torso_emb, legs_emb]

def match_with_occlusion(part_embs1, part_embs2):
    # Porównaj tylko widoczne części
    similarities = []
    for p1, p2 in zip(part_embs1, part_embs2):
        if p1 is not None and p2 is not None:
            similarities.append(cosine_sim(p1, p2))
    return np.mean(similarities)

Efekt: Robustness przy zasłonięciach


🔧 Konfiguracja dla Różnych Scenariuszy

Scenariusz 1: Wysoka Precyzja (Bank, Lotnisko)

config = {
    'similarity_threshold': 0.6,  # Wyższy - mniej false positives
    'alpha': 0.2,  # Wolniejsza adaptacja
    'inactive_timeout': 60.0,  # Dłuższa pamięć
    'min_appearances': 3,  # Potwierdź ID po 3 widzeniach
    'use_face': True,  # Zawsze używaj face
    'yolo_confidence': 0.7  # Wyższa pewność YOLO
}

Scenariusz 2: Wysoka Wydajność (Sklep, Parking)

config = {
    'similarity_threshold': 0.45,  # Niższy - więcej true positives
    'alpha': 0.3,  # Szybsza adaptacja
    'inactive_timeout': 20.0,  # Szybkie czyszczenie
    'yolo_imgsz': 320,  # Mniejszy rozmiar
    'skip_frames': 3,  # Co 3 klatka
    'use_face': False,  # Tylko body dla szybkości
}

Scenariusz 3: Multi-Person Crowded (Koncert, Stadion)

config = {
    'similarity_threshold': 0.55,
    'use_hungarian': True,  # Zawsze Hungarian
    'max_gallery_size': 50,  # Limit galerii
    'spatial_constraint': True,  # IoU filtering
    'temporal_smoothing': True,  # Kalman filter
    'batch_inference': True  # Batch processing dla GPU
}

📊 Benchmarking Recommendations

Test 1: Single Person Persistence

# Wejdź → znikij → wróć po 10s → sprawdź ID
def test_persistence():
    id1 = first_appearance_id()
    time.sleep(10)
    id2 = reappearance_id()
    assert id1 == id2, "ID should persist"

Test 2: Multi-Person Uniqueness

# 3 osoby w kadrze → każda unikalne ID
def test_uniqueness():
    ids = detect_all_ids_in_frame()
    assert len(ids) == len(set(ids)), "All IDs should be unique"

Test 3: Clothing Change Robustness

# Osoba zmienia kurtkę → sprawdź czy ID się utrzymuje
# (wymaga face detection lub pose consistency)

🐛 Debugging Tips

Problem: ID migają (1→2→1→2)

Przyczyna: Threshold zbyt blisko embedding similarity
Fix: Obniż threshold o 0.05 lub zwiększ alpha

Problem: Wszystkie osoby dostają nowe ID

Przyczyna: Embeddingi zbyt różne (zły lighting, blur)
Fix: Sprawdź jakość obrazu, zwiększ YOLO imgsz, użyj denoising

Problem: FPS zbyt niski

Przyczyna: CPU bottleneck
Fix: Zwiększ skip_frames, zmniejsz yolo_imgsz, użyj GPU, użyj osnet_x0_25

Problem: Galeria rośnie bez końca

Przyczyna: clean_inactive_persons nie działa
Fix: Sprawdź czy clean jest wywoływany, zmniejsz inactive_timeout


🎓 Recommended Papers

  1. "Omni-Scale Feature Learning for Person Re-Identification" - OSNet paper
  2. "Deep Learning for Person Re-identification: A Survey" - Comprehensive overview
  3. "Multi-Object Tracking with Multiple Cues and Switcher-Aware Classification" - Tracking theory
  4. "Simple Online and Realtime Tracking with a Deep Association Metric (Deep SORT)" - Association logic

🚦 Priority Roadmap

High Priority (Implement Next)

  1. ✅ Temporal smoothing (Kalman/voting)
  2. ✅ Spatial constraint (IoU filtering)
  3. ✅ GPU batch inference

Medium Priority

  1. Adaptive threshold
  2. OSNet lightweight model
  3. Feature caching

Low Priority (Nice to Have)

  1. Multi-camera fusion
  2. Pose-aware ReID
  3. Attention mechanism
  4. Part-based matching

💡 Pro Tips

  1. Zawsze monitoruj FPS - jeśli <5 FPS, użytkownicy będą niezadowoleni
  2. Log wszystkie ID transitions - pomaga w debugging (ID 1→2, dlaczego?)
  3. Zapisuj edge cases - gdy similarity jest blisko threshold (0.48-0.52)
  4. Test na różnych kamerach - każda ma inne lighting/resolution
  5. Używaj confusion matrix - track false positives i false negatives

📝 Przykładowa Metryka Sukcesu

class ReIDMetrics:
    def __init__(self):
        self.id_switches = 0  # Ile razy ID się zmieniło niepotrzebnie
        self.id_persistence = []  # Ile sekund ID się utrzymuje
        self.false_merges = 0  # Różne osoby z tym samym ID
        self.false_splits = 0  # Ta sama osoba z różnymi ID
        
    def report(self):
        print(f"ID Switches: {self.id_switches}")
        print(f"Avg Persistence: {np.mean(self.id_persistence):.1f}s")
        print(f"False Merges: {self.false_merges}")
        print(f"False Splits: {self.false_splits}")

Target Values:

  • ID Switches: <5% of total detections
  • Persistence: >30s average
  • False Merges: 0 (critical!)
  • False Splits: <10% (akceptowalne dla powracających osób)