SIFT — наиболее точный, ORB — самый быстрый, AKAZE — баланс
FLANN быстрее на больших наборах, BFMatcher точнее на малых
0.75 Низкий порог (0.5) — строгая фильтрация, высокий (0.9) — больше совпадений
Для визуализации обычно достаточно 100–300

Справка

СценарийДетекторМатчерПорог
Панорамная склейкаSIFTFLANN0.7
Поиск объекта на фотоORBBFMatcher0.75
Быстрое сравнениеBRISKBFMatcher0.8
Максимальная точностьSIFTFLANN0.6

Параметры запроса

ПараметрТипПо умолчаниюОписание
queryfileИзображение-запрос (обязательный)
trainfileЭталонное изображение (обязательный)
detectorstringSIFTSIFT | ORB | AKAZE | BRISK
matcherstringFLANNFLANN | BFMatcher
ratio_thresholdfloat0.75Порог Lowe's ratio (0.1–1.0)
max_matchesint200Максимум совпадений (1–5000)

Примеры запросов и ответов

# ── Запрос: отправка двух изображений ──
curl -X POST http://localhost:8888/api/v1/match/feature \
  -F "query_image=@photo1.jpg" \
  -F "train_image=@photo2.jpg"

# ── Ответ (201): задача создана ──
# {
#   "uid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
#   "status": "queued",
#   "tool": "feature",
#   "params": {"detector":"SIFT","matcher":"FLANN","ratio_threshold":0.75,"max_matches":200},
#   "created_at": "2026-03-25T12:00:00",
#   "input_files": ["uploads/f47ac.../photo1.jpg","uploads/f47ac.../photo2.jpg"],
#   "thumbnails": ["thumbnails/f47ac.../photo1.jpg","thumbnails/f47ac.../photo2.jpg"],
#   "result_files": [],
#   "result_data": {}
# }

# ── Запрос: получить результат задачи ──
curl http://localhost:8888/api/v1/tasks/f47ac10b-58cc-4372-a567-0e02b2c3d479

# ── Ответ (200): задача завершена ──
# {
#   "uid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
#   "status": "done",
#   "tool": "feature",
#   "params": {"detector":"SIFT","matcher":"FLANN","ratio_threshold":0.75,"max_matches":200},
#   "result_files": ["results/f47ac.../matches.png"],
#   "result_data": {
#     "query_keypoints": 1523,
#     "train_keypoints": 1847,
#     "good_matches": 342,
#     "detector": "SIFT",
#     "matcher": "FLANN"
#   }
# }

# ── Запрос: обновить настройки ──
curl -X PUT http://localhost:8888/api/v1/settings/feature \
  -H "Content-Type: application/json" \
  -d '{"detector":"SIFT","matcher":"FLANN","ratio_threshold":0.7,"max_matches":200}'

# ── Ответ (200) ──
# {
#   "tool": "feature",
#   "params": {"detector":"SIFT","matcher":"FLANN","ratio_threshold":0.7,"max_matches":200}
# }
// ── Запрос: отправка изображений ──
const form = new FormData();
form.append("query_image", fileInput1.files[0]);
form.append("train_image", fileInput2.files[0]);
form.append("detector", "SIFT");
form.append("ratio_threshold", "0.75");

const res = await fetch("/api/v1/match/feature", {
  method: "POST",
  body: form,
});
const task = await res.json();

// ── Ответ task: ──
// {
//   uid: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
//   status: "queued",
//   tool: "feature",
//   params: {detector:"SIFT", matcher:"FLANN", ratio_threshold:0.75, max_matches:200},
//   input_files: ["uploads/f47ac.../photo1.jpg", "uploads/f47ac.../photo2.jpg"],
//   result_files: [], result_data: {}
// }

// ── Запрос: получить результат ──
const status = await fetch(`/api/v1/tasks/${task.uid}`);
const result = await status.json();

// ── Ответ result (когда done): ──
// {
//   uid: "f47ac10b-...",
//   status: "done",
//   result_files: ["results/f47ac.../matches.png"],
//   result_data: {
//     query_keypoints: 1523, train_keypoints: 1847,
//     good_matches: 342, detector: "SIFT", matcher: "FLANN"
//   }
// }
import requests

# ── Запрос: отправка изображений ──
files = {
    "query_image": open("photo1.jpg", "rb"),
    "train_image": open("photo2.jpg", "rb"),
}
data = {
    "detector": "SIFT",
    "matcher": "FLANN",
    "ratio_threshold": "0.75",
    "max_matches": "200",
}
resp = requests.post(
    "http://localhost:8888/api/v1/match/feature",
    files=files, data=data,
)
task = resp.json()

# ── Ответ task: ──
# {
#   "uid": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
#   "status": "queued",
#   "tool": "feature",
#   "params": {"detector":"SIFT","matcher":"FLANN","ratio_threshold":0.75,"max_matches":200},
#   "input_files": ["uploads/f47ac.../photo1.jpg","uploads/f47ac.../photo2.jpg"],
#   "result_files": [], "result_data": {}
# }

# ── Запрос: получить результат ──
result = requests.get(
    f"http://localhost:8888/api/v1/tasks/{task['uid']}"
).json()

# ── Ответ result (когда done): ──
# {
#   "uid": "f47ac10b-...",
#   "status": "done",
#   "result_files": ["results/f47ac.../matches.png"],
#   "result_data": {
#     "query_keypoints": 1523, "train_keypoints": 1847,
#     "good_matches": 342, "detector": "SIFT", "matcher": "FLANN"
#   }
# }

Пример результата

Query
Query (искомый объект)
Train
Train (сцена)
Результат: совпадения
matches.png — совпадения

Документация OpenCV →