Feature-Homography Matching
Локализация объекта через гомографию: SIFT/ORB/AKAZE/BRISK + RANSAC + perspectiveTransform
Справка
| Сценарий | Детектор | RANSAC | Min inliers | Ratio |
|---|---|---|---|---|
| Аэросъёмка (разные даты) | SIFT | 5.0 | 4 | 0.82 |
| Одинаковый масштаб | SIFT | 3.0 | 6 | 0.75 |
| Быстрый поиск | ORB | 5.0 | 4 | 0.85 |
| Максимальная точность | SIFT | 2.0 | 10 | 0.65 |
| Сезонные различия | AKAZE | 8.0 | 4 | 0.85 |
Автоматический fallback: если основной детектор даёт мало матчей, автоматически пробуются SIFT → AKAZE → ORB.
При провале RANSAC пробуется LMEDS, затем rigid translation (медианный сдвиг).
CLAHE нормализует освещение между снимками.
Алгоритм
- CLAHE — нормализация освещения
- Multi-detector: детекция ключевых точек (с автоматическим fallback)
- KNN-сопоставление + Lowe's ratio test → good_matches
cv2.findHomography(RANSAC)→ матрица H + инлаеры- Fallback: LMEDS → rigid translation (если RANSAC не сработал)
cv2.perspectiveTransform(corners, H)→ контур объекта- Геометрическая валидация (выпуклость, площадь, границы)
Параметры запроса
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
| query_image | file | — | Искомый объект (template) |
| train_image | file | — | Сцена для поиска (scene) |
| detector | string | SIFT | SIFT | ORB | AKAZE | BRISK |
| matcher | string | FLANN | FLANN | BFMatcher |
| ratio_threshold | float | 0.82 | Порог Lowe's ratio (0.1–1.0) |
| max_matches | int | 1000 | Макс. совпадений (1–5000) |
| ransac_reproj_threshold | float | 5.0 | Ошибка репроекции (px) |
| max_iters | int | 5000 | Макс. итераций RANSAC |
| confidence | float | 0.999 | Уверенность RANSAC |
| min_inliers | int | 4 | Мин. инлаеров (≥4) |
| min_inlier_ratio | float | 0.08 | Мин. доля инлаеров |
| return_debug_images | bool | true | Генерировать отладочные изображения |
Примеры запросов и ответов
# ── Запрос: локализация объекта через гомографию ── curl -X POST http://localhost:8888/api/v1/match/feature-homography \ -F "query_image=@template.png" \ -F "train_image=@scene.png" \ -F "detector=SIFT" \ -F "matcher=FLANN" \ -F "ratio_threshold=0.82" \ -F "ransac_reproj_threshold=5.0" \ -F "max_iters=5000" \ -F "min_inliers=4" # ── Ответ (201): задача создана ── # { # "uid": "a52a88d9-8fb7-4c17-b08c-200bfb3d5fb8", # "status": "queued", # "tool": "feature_homography", # "params": { "detector":"SIFT", "ransac_reproj_threshold":5.0, ... } # } # ── Запрос: получить результат ── curl http://localhost:8888/api/v1/tasks/a52a88d9-8fb7-4c17-b08c-200bfb3d5fb8 # ── Ответ (200): задача завершена ── # { # "status": "done", # "result_data": { # "object_found": true, # "inliers_count": 72, # "inlier_ratio": 0.878, # "projected_corners": [ # {"x": 439, "y": 239}, {"x": 575, "y": 239}, # {"x": 575, "y": 374}, {"x": 439, "y": 374} # ], # "bbox": {"x": 439, "y": 239, "w": 136, "h": 135}, # "homography": [[...], [...], [...]], # "metrics": {"mean_reprojection_error": 0.166} # }, # "result_files": [ # "results/a52a.../inlier_matches.png", # "results/a52a.../detected_object.png" # ] # }
// ── Запрос: локализация через гомографию ── const form = new FormData(); form.append("query_image", templateFile); form.append("train_image", sceneFile); form.append("detector", "SIFT"); form.append("ransac_reproj_threshold", "5.0"); form.append("min_inliers", "4"); const res = await fetch("/api/v1/match/feature-homography", { method: "POST", body: form, }); const task = await res.json(); // ── Поллинг результата ── const result = await fetch(`/api/v1/tasks/${task.uid}`) .then(r => r.json()); // result.result_data: // { // object_found: true, // inliers_count: 72, // inlier_ratio: 0.878, // projected_corners: [{x:439,y:239}, ...], // bbox: {x:439, y:239, w:136, h:135}, // metrics: {mean_reprojection_error: 0.166} // }
import requests # ── Запрос: локализация через гомографию ── files = { "query_image": open("template.png", "rb"), "train_image": open("scene.png", "rb"), } data = { "detector": "SIFT", "ransac_reproj_threshold": "5.0", "max_iters": "5000", "min_inliers": "4", } resp = requests.post( "http://localhost:8888/api/v1/match/feature-homography", files=files, data=data, ) task = resp.json() # ── Поллинг результата ── result = requests.get( f"http://localhost:8888/api/v1/tasks/{task['uid']}" ).json() # result["result_data"]: # { # "object_found": True, # "inliers_count": 72, # "inlier_ratio": 0.878, # "projected_corners": [{"x":439,"y":239}, ...], # "bbox": {"x":439, "y":239, "w":136, "h":135}, # "homography": [[...], [...], [...]], # "metrics": {"mean_reprojection_error": 0.166} # }