Surface Matching
3D-облака точек: PPF (Point Pair Features) + ICP
Справка
Пример входных данных и результата
Surface Matching работает с 3D-облаками точек в формате PLY (с нормалями). Результат — матрица трансформации 4×4, переводящая координаты модели в координаты сцены.
Сцена
Модель
| Сценарий | Sampling | ICP | Итерации |
|---|---|---|---|
| Точное сопоставление | 0.025 | Да | 200 |
| Быстрый поиск | 0.1 | Нет | — |
| Шумные данные | 0.05 | Да | 300 |
| Большие модели | 0.08 | Да | 150 |
Параметры запроса
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
| model | file | — | 3D-модель (.ply) — обязательный |
| scene | file | — | 3D-сцена (.ply) — обязательный |
| relative_sampling_step | float | 0.05 | Шаг дискретизации (0.01–0.5) |
| relative_distance_step | float | 0.05 | Шаг расстояния PPF (0.01–0.5) |
| num_angles | int | 30 | Углы дискретизации (1–360) |
| use_icp | bool | true | Уточнение через ICP |
| icp_tolerance | float | 0.005 | Порог сходимости ICP |
| icp_iterations | int | 100 | Макс. итераций ICP (1–1000) |
Примеры запросов и ответов
# ── Запрос: отправка PLY-файлов ── curl -X POST http://localhost:8888/api/v1/match/surface \ -F "model_file=@model.ply" \ -F "scene_file=@scene.ply" # ── Ответ (201): задача создана ── # { # "uid": "c9a1e2b3-4d5f-6789-abcd-ef0123456789", # "status": "queued", # "tool": "surface", # "params": { # "relative_sampling_step": 0.05, "relative_distance_step": 0.05, # "num_angles": 30, "use_icp": true, "icp_tolerance": 0.005, "icp_iterations": 100 # }, # "created_at": "2026-03-25T14:30:00", # "input_files": ["uploads/c9a1e.../model.ply","uploads/c9a1e.../scene.ply"], # "result_files": [], "result_data": {} # } # ── Запрос: получить результат ── curl http://localhost:8888/api/v1/tasks/c9a1e2b3-4d5f-6789-abcd-ef0123456789 # ── Ответ (200): задача завершена ── # { # "uid": "c9a1e2b3-4d5f-6789-abcd-ef0123456789", # "status": "done", # "tool": "surface", # "result_files": ["results/c9a1e.../pose.json"], # "result_data": { # "pose": [[0.99,-0.01,0.03,12.5],[0.01,0.99,-0.02,-3.1],[-0.03,0.02,0.99,0.8],[0,0,0,1]], # "num_model_points": 4521, "num_scene_points": 18230, "icp_residual": 0.0023 # } # } # ── Запрос: обновить настройки ── curl -X PUT http://localhost:8888/api/v1/settings/surface \ -H "Content-Type: application/json" \ -d '{"relative_sampling_step":0.05,"relative_distance_step":0.05,"num_angles":30,"use_icp":true,"icp_tolerance":0.005,"icp_iterations":100}' # ── Ответ (200) ── # { # "tool": "surface", # "params": {"relative_sampling_step":0.05,"relative_distance_step":0.05,"num_angles":30,"use_icp":true,"icp_tolerance":0.005,"icp_iterations":100} # }
// ── Запрос: отправка PLY-файлов ── const form = new FormData(); form.append("model_file", modelFile); form.append("scene_file", sceneFile); form.append("relative_sampling_step", "0.025"); form.append("use_icp", "true"); const res = await fetch("/api/v1/match/surface", { method: "POST", body: form, }); const task = await res.json(); // ── Ответ task: ── // { // uid: "c9a1e2b3-4d5f-6789-abcd-ef0123456789", // status: "queued", // tool: "surface", // params: {relative_sampling_step:0.025, use_icp:true, ...}, // input_files: ["uploads/c9a1e.../model.ply","uploads/c9a1e.../scene.ply"], // result_files: [], result_data: {} // } // ── Запрос: получить результат ── const status = await fetch(`/api/v1/tasks/${task.uid}`); const result = await status.json(); // ── Ответ result (когда done): ── // { // uid: "c9a1e2b3-...", // status: "done", // result_files: ["results/c9a1e.../pose.json"], // result_data: { // pose: [[0.99,-0.01,0.03,12.5],[0.01,0.99,-0.02,-3.1],[-0.03,0.02,0.99,0.8],[0,0,0,1]], // num_model_points: 4521, num_scene_points: 18230, icp_residual: 0.0023 // } // }
import requests # ── Запрос: отправка PLY-файлов ── files = { "model_file": open("model.ply", "rb"), "scene_file": open("scene.ply", "rb"), } data = { "relative_sampling_step": "0.025", "num_angles": "60", "use_icp": "true", "icp_iterations": "200", } resp = requests.post( "http://localhost:8888/api/v1/match/surface", files=files, data=data, ) task = resp.json() # ── Ответ task: ── # { # "uid": "c9a1e2b3-4d5f-6789-abcd-ef0123456789", # "status": "queued", # "tool": "surface", # "params": {"relative_sampling_step":0.025,"num_angles":60,"use_icp":true,"icp_iterations":200,...}, # "input_files": ["uploads/c9a1e.../model.ply","uploads/c9a1e.../scene.ply"], # "result_files": [], "result_data": {} # } # ── Запрос: получить результат ── result = requests.get( f"http://localhost:8888/api/v1/tasks/{task['uid']}" ).json() # ── Ответ result (когда done): ── # { # "uid": "c9a1e2b3-...", # "status": "done", # "result_files": ["results/c9a1e.../pose.json"], # "result_data": { # "pose": [[0.99,-0.01,0.03,12.5],[0.01,0.99,-0.02,-3.1],[-0.03,0.02,0.99,0.8],[0,0,0,1]], # "num_model_points": 4521, "num_scene_points": 18230, "icp_residual": 0.0023 # } # }