# ============================================================ # HOMOGRAPHY_INTEGRATION.py # Как подключить homography-enabled motion saliency в main.py # ============================================================ # ───────────────────────────────────────────────────────────── # ИМПОРТЫ # ───────────────────────────────────────────────────────────── """ from camera_motion import ( estimate_ego_motion_adaptive, estimate_global_homography_ex, estimate_global_affine_ex, homography_is_plausible, affine_is_plausible, warp_is_plausible, ) from motion_saliency import MotionSaliency """ # ───────────────────────────────────────────────────────────── # ВАРИАНТ A (простой, рекомендуется начать с него): # adaptive — homography с автоматическим fallback на affine # ───────────────────────────────────────────────────────────── """ motion_sal = MotionSaliency() prev_gray_for_ms = None ms_kind_stats = {"homography": 0, "affine": 0, "none": 0} # В главном цикле: gray_eff = cv2.cvtColor(frame_eff_bgr, cv2.COLOR_BGR2GRAY) M, kind, ego_inliers, ego_outliers = estimate_ego_motion_adaptive( prev_gray_for_ms, gray_eff ) ms_kind_stats[kind] = ms_kind_stats.get(kind, 0) + 1 if not warp_is_plausible(M, kind): M = None kind = "none" ego_outliers = np.zeros((0, 2), dtype=np.float32) # В терминальной фазе отключаем — слишком большой parallax ms_active = True if intercept_params is not None: phase = intercept_params.get("phase", "") if phase in ("terminal", "critical"): ms_active = False if ms_active: motion_sal.update(gray_eff, prev_gray_for_ms, M, kind=kind, outlier_pts=ego_outliers) else: motion_sal.reset() prev_gray_for_ms = gray_eff """ # ───────────────────────────────────────────────────────────── # ВАРИАНТ B (dual residual, максимально устойчивый): # считаем обе модели, берём min residual. Дороже по CPU но # устойчивее на сценах с несколькими плоскостями (горизонт+земля). # ───────────────────────────────────────────────────────────── """ # Считаем обе модели H, h_in, h_out, h_meta = estimate_global_homography_ex( prev_gray_for_ms, gray_eff ) A, a_in, a_out = estimate_global_affine_ex( prev_gray_for_ms, gray_eff ) # Фильтруем по качеству H_use = H if homography_is_plausible(H, h_meta) else None A_use = A if affine_is_plausible(A) else None # Объединяем outliers обеих моделей (точки, не объяснённые НИ одной) # — пересечение дает самый чистый сигнал, но можно и объединение if len(h_out) > 0 and len(a_out) > 0: # Простое объединение обычно достаточно ego_outliers = np.concatenate([h_out, a_out], axis=0) elif len(h_out) > 0: ego_outliers = h_out elif len(a_out) > 0: ego_outliers = a_out else: ego_outliers = np.zeros((0, 2), dtype=np.float32) motion_sal.update_dual( gray_eff, prev_gray_for_ms, H=H_use, A=A_use, outlier_pts=ego_outliers, ) prev_gray_for_ms = gray_eff """ # ───────────────────────────────────────────────────────────── # ТЮНИНГ ПО СЦЕНАМ # ───────────────────────────────────────────────────────────── # # 1) Если в логах ms_kind_stats почти всегда "affine" — значит # homography не проходит пороги качества. Варианты: # - понизить HOMO_MIN_INLIERS до 35 # - понизить HOMO_MIN_FILLED_CELLS до 3 # - увеличить CAM_MOTION_MAX_CORNERS чтобы было больше точек # # 2) Если homography выбирается часто но daёт странные residual # (вся земля горит) — это признак того, что она фитится в # одну плоскость (небо), а земля как вторая плоскость даёт # fake residual. Решение — переключиться на Variant B (dual), # min-из-двух карт это исправляет. # # 3) Для сложных сцен с ЛЭП и лесом рекомендую сразу Variant B. # Overhead ~2x по CPU для ego-motion, но motion_saliency # всё равно дешёвый относительно YOLO, общий FPS просядет # минимально. # # 4) Если летите очень низко над лесом (parallax огромный, никакая # модель не помогает) — тогда motion saliency просто отключайте # на такие периоды по высоте/скорости носителя, а полагайтесь # на KLT+IMM которые у вас уже есть. # # 5) Если homography выдаёт вырожденные матрицы часто — уменьшите # HOMO_MAX_CORNER_SHIFT до 80, это агрессивнее отсеивает плохие # фиты (ценой иногда пропустить валидный резкий манёвр камеры). # ───────────────────────────────────────────────────────────── # ДИАГНОСТИКА # ───────────────────────────────────────────────────────────── # В конце сессии напечатайте статистику: """ total = sum(ms_kind_stats.values()) if total > 0: print(f"Ego motion stats: " f"homography={ms_kind_stats['homography']/total*100:.1f}% " f"affine={ms_kind_stats['affine']/total*100:.1f}% " f"none={ms_kind_stats['none']/total*100:.1f}%") """ # Здоровая статистика на вашем типе данных: # homography: 50-80% (когда хватает текстуры) # affine: 15-40% (чистое небо, слабая текстура) # none: < 5% (если больше — проблема с LK/feature detection)