#include #include #include #include #include #include #include #include #include namespace { struct Observation { uint64_t track_id; uint64_t image_id; uint64_t feature_set_id; uint32_t feature_index; Lardon3DSparseGeometryPoint2 pixel; }; struct Track { uint64_t id; std::vector observations; }; struct Image { uint64_t id; Lardon3DSparseGeometryCalibration calibration; size_t component; }; struct Pose { uint64_t image_id; Lardon3DSparseGeometryPose value; }; struct Landmark { uint64_t track_id; uint64_t component_key; Lardon3DSparseGeometryPoint3 point; std::vector observations; double rmse; double median; }; struct Component { uint64_t key; std::vector image_indices; }; struct UnionFind { std::vector parent; explicit UnionFind(size_t count) : parent(count) { for (size_t index = 0; index < count; ++index) parent[index] = index; } size_t find(size_t value) { size_t root = value; while (parent[root] != root) root = parent[root]; while (parent[value] != value) { size_t next = parent[value]; parent[value] = root; value = next; } return root; } void unite(size_t left, size_t right) { left = find(left); right = find(right); if (left == right) return; if (left < right) parent[right] = left; else parent[left] = right; } }; bool finite_calibration(const Lardon3DSparseGeometryCalibration &calibration) { return calibration.width > 0 && calibration.height > 0 && std::isfinite(calibration.fx) && std::isfinite(calibration.fy) && std::isfinite(calibration.cx) && std::isfinite(calibration.cy) && std::isfinite(calibration.k1) && std::isfinite(calibration.k2) && std::isfinite(calibration.p1) && std::isfinite(calibration.p2) && calibration.fx > 0.0 && calibration.fy > 0.0 && calibration.cx >= 0.0 && calibration.cx < calibration.width && calibration.cy >= 0.0 && calibration.cy < calibration.height; } bool finite_pose(const Lardon3DSparseGeometryPose &pose) { for (double value : pose.rotation_cw) if (!std::isfinite(value)) return false; for (double value : pose.translation_cw) if (!std::isfinite(value)) return false; return true; } Lardon3DSparseGeometryPose identity_pose() { return {{1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}}; } bool project(const Image &image, const Pose &pose, const Lardon3DSparseGeometryPoint3 &point, Lardon3DSparseGeometryPoint2 *pixel) { const double *r = pose.value.rotation_cw; const double *t = pose.value.translation_cw; const double x = r[0] * point.x + r[1] * point.y + r[2] * point.z + t[0]; const double y = r[3] * point.x + r[4] * point.y + r[5] * point.z + t[1]; const double z = r[6] * point.x + r[7] * point.y + r[8] * point.z + t[2]; if (!pixel || !std::isfinite(x) || !std::isfinite(y) || !std::isfinite(z) || z <= 1e-9) return false; pixel->x = image.calibration.fx * x / z + image.calibration.cx; pixel->y = image.calibration.fy * y / z + image.calibration.cy; return std::isfinite(pixel->x) && std::isfinite(pixel->y); } double reprojection_error(const Image &image, const Pose &pose, const Landmark &landmark, const Observation &observation) { Lardon3DSparseGeometryPoint2 projected; if (!project(image, pose, landmark.point, &projected)) return INFINITY; const double dx = projected.x - observation.pixel.x; const double dy = projected.y - observation.pixel.y; return std::sqrt(dx * dx + dy * dy); } bool validate_parameters(const Lardon3DSparseIncrementalParameters &p) { return p.minimum_seed_tracks >= 2 && p.minimum_seed_landmarks >= 1 && p.minimum_pnp_correspondences >= 4 && p.maximum_seed_candidates > 0 && p.maximum_registration_rounds > 0 && p.maximum_landmarks_per_round > 0 && p.maximum_images >= 2 && p.maximum_observations >= p.maximum_tracks && std::isfinite(p.reprojection_threshold_px) && p.reprojection_threshold_px > 0.0 && std::isfinite(p.minimum_track_parallax_rad) && p.minimum_track_parallax_rad >= 0.0 && p.relative_pose.minimum_inliers >= p.minimum_seed_tracks && p.pnp.minimum_inliers >= p.minimum_pnp_correspondences && p.relative_pose.minimum_inlier_ratio > 0.0 && p.relative_pose.minimum_inlier_ratio <= 1.0 && p.pnp.minimum_inlier_ratio > 0.0 && p.pnp.minimum_inlier_ratio <= 1.0; } void destroy_result(Lardon3DSparseIncrementalResult *result) { if (!result) return; std::free(result->components); std::free(result->cameras); std::free(result->landmarks); std::free(result->observations); std::free(result->unregistered_images); *result = {}; } bool append_component(const Component &component, const std::vector &images, std::vector *out) { Lardon3DSparseIncrementalComponent value = {}; value.component_key = component.key; value.image_count = component.image_indices.size(); for (size_t index : component.image_indices) if (images[index].id == component.key) value.component_key = images[index].id; out->push_back(value); return true; } enum class LandmarkCandidateStatus { accepted, insufficient, triangulation_failed, behind_camera, reprojection_failed, }; LandmarkCandidateStatus build_landmark_candidate( const Track &track, uint64_t component_key, const std::vector &images, const std::map &image_index, const std::vector &cameras, const std::set ®istered, const Lardon3DSparseIncrementalParameters ¶meters, Landmark *candidate, Lardon3DSparseGeometryResult *geometry_status, Lardon3DSparseIncrementalResult *result) { std::vector normalized; std::vector poses; std::vector used; for (const Observation &observation : track.observations) { if (!registered.count(observation.image_id)) continue; const auto pose_it = std::find_if( cameras.begin(), cameras.end(), [&](const Pose &pose) { return pose.image_id == observation.image_id; }); if (pose_it == cameras.end()) continue; const Image &image = images[image_index.at(observation.image_id)]; Lardon3DSparseGeometryPoint2 point; if (lardon3d_sparse_geometry_normalize( &image.calibration, &observation.pixel, 1, &point) != LARDON3D_SPARSE_GEOMETRY_OK) return LandmarkCandidateStatus::triangulation_failed; normalized.push_back(point); poses.push_back(pose_it->value); used.push_back(observation); } if (used.size() < 2) return LandmarkCandidateStatus::insufficient; Lardon3DSparseGeometryPoint3 point; *geometry_status = lardon3d_sparse_geometry_triangulate_multi_view( normalized.data(), poses.data(), normalized.size(), &point); if (*geometry_status != LARDON3D_SPARSE_GEOMETRY_OK) { return *geometry_status == LARDON3D_SPARSE_GEOMETRY_CHEIRALITY_FAILED ? LandmarkCandidateStatus::behind_camera : LandmarkCandidateStatus::triangulation_failed; } Lardon3DSparseGeometryPoint3 refined; ++result->point_refinement_attempts; const Lardon3DSparseGeometryResult refinement_status = lardon3d_sparse_geometry_refine_point( normalized.data(), poses.data(), normalized.size(), &point, ¶meters.refinement, &refined); if (refinement_status == LARDON3D_SPARSE_GEOMETRY_OK) { point = refined; ++result->point_refinement_successes; } Landmark replacement = { track.id, component_key, point, std::move(used), 0.0, 0.0}; double squared = 0.0; double maximum = 0.0; for (const Observation &observation : replacement.observations) { const Image &image = images[image_index.at(observation.image_id)]; const Pose &pose = *std::find_if( cameras.begin(), cameras.end(), [&](const Pose &item) { return item.image_id == observation.image_id; }); const double error = reprojection_error( image, pose, replacement, observation); if (!std::isfinite(error)) return LandmarkCandidateStatus::behind_camera; if (error > parameters.reprojection_threshold_px) return LandmarkCandidateStatus::reprojection_failed; squared += error * error; maximum = std::max(maximum, error); } replacement.rmse = std::sqrt( squared / static_cast(replacement.observations.size())); replacement.median = maximum; *candidate = std::move(replacement); return LandmarkCandidateStatus::accepted; } void record_landmark_rejection(LandmarkCandidateStatus status, Lardon3DSparseIncrementalResult *result) { if (status == LandmarkCandidateStatus::behind_camera) { ++result->rejected_landmarks; ++result->rejected_behind_camera; } else if (status == LandmarkCandidateStatus::reprojection_failed) { ++result->rejected_landmarks; ++result->rejected_reprojection; } else if (status == LandmarkCandidateStatus::triangulation_failed) { ++result->triangulation_failures; } } } // namespace extern "C" bool lardon3d_sparse_incremental_parameters_default( Lardon3DSparseIncrementalParameters *parameters) { if (!parameters) return false; *parameters = {}; parameters->minimum_seed_tracks = 6; parameters->minimum_seed_landmarks = 6; parameters->minimum_pnp_correspondences = 6; parameters->maximum_seed_candidates = 32; parameters->maximum_registration_rounds = 32; parameters->maximum_landmarks_per_round = 4096; parameters->maximum_images = 4096; parameters->maximum_observations = 1000000; parameters->maximum_tracks = 250000; parameters->reprojection_threshold_px = 2.0; parameters->minimum_track_parallax_rad = 1e-4; parameters->relative_pose = {1.5, 0.999, 1500, 6, 0.5, 1e-4, 0.5, 0}; parameters->pnp = {1.5, 0.999, 1000, 6, 0.5, 0}; parameters->refinement = {30, 1e-12}; return true; } extern "C" Lardon3DSparseIncrementalStatus lardon3d_sparse_incremental_run( const Lardon3DSparseIncrementalInput *input, const Lardon3DSparseIncrementalParameters *parameters, Lardon3DSparseIncrementalResult *result) { if (!result) return LARDON3D_SPARSE_INCREMENTAL_INVALID_ARGUMENT; destroy_result(result); try { if (!input || !parameters || !validate_parameters(*parameters) || !input->images || input->image_count == 0 || !input->observations || input->observation_count == 0 || input->image_count > parameters->maximum_images || input->observation_count > parameters->maximum_observations || input->track_set_id == 0 || input->calibration_scope_id == 0) return LARDON3D_SPARSE_INCREMENTAL_INVALID_ARGUMENT; std::vector images; images.reserve(input->image_count); for (size_t index = 0; index < input->image_count; ++index) { const auto &source = input->images[index]; if (source.image_id == 0 || !finite_calibration(source.calibration)) return LARDON3D_SPARSE_INCREMENTAL_INVALID_ARGUMENT; images.push_back({source.image_id, source.calibration, 0}); } std::sort(images.begin(), images.end(), [](const Image &a, const Image &b) { return a.id < b.id; }); for (size_t index = 1; index < images.size(); ++index) if (images[index - 1].id == images[index].id) return LARDON3D_SPARSE_INCREMENTAL_INVALID_ARGUMENT; std::map image_index; for (size_t index = 0; index < images.size(); ++index) image_index[images[index].id] = index; std::map tracks_by_id; for (size_t index = 0; index < input->observation_count; ++index) { const auto &source = input->observations[index]; if (source.track_id == 0 || source.image_id == 0 || source.feature_set_id == 0 || source.feature_index >= source.feature_count || image_index.find(source.image_id) == image_index.end() || !std::isfinite(source.x) || !std::isfinite(source.y)) return LARDON3D_SPARSE_INCREMENTAL_INVALID_ARGUMENT; Track &track = tracks_by_id[source.track_id]; track.id = source.track_id; for (const Observation &old : track.observations) if (old.image_id == source.image_id) return LARDON3D_SPARSE_INCREMENTAL_INVALID_ARGUMENT; track.observations.push_back( {source.track_id, source.image_id, source.feature_set_id, source.feature_index, {source.x, source.y}}); } if (tracks_by_id.empty() || tracks_by_id.size() > parameters->maximum_tracks) return LARDON3D_SPARSE_INCREMENTAL_FAILED; std::set> feature_observations; for (const auto &entry : tracks_by_id) { if (entry.second.observations.size() < 2) return LARDON3D_SPARSE_INCREMENTAL_INVALID_ARGUMENT; for (const Observation &observation : entry.second.observations) if (!feature_observations.insert( {observation.feature_set_id, observation.feature_index}) .second) return LARDON3D_SPARSE_INCREMENTAL_INVALID_ARGUMENT; } for (auto &entry : tracks_by_id) { std::sort(entry.second.observations.begin(), entry.second.observations.end(), [](const Observation &left, const Observation &right) { return left.image_id < right.image_id; }); } UnionFind union_find(images.size()); for (const auto &entry : tracks_by_id) { const auto &observations = entry.second.observations; for (size_t index = 1; index < observations.size(); ++index) union_find.unite(image_index[observations[0].image_id], image_index[observations[index].image_id]); } std::map components_by_root; for (size_t index = 0; index < images.size(); ++index) components_by_root[union_find.find(index)].image_indices.push_back(index); std::vector components; for (auto &entry : components_by_root) { auto &items = entry.second.image_indices; std::sort(items.begin(), items.end(), [&](size_t a, size_t b) { return images[a].id < images[b].id; }); entry.second.key = images[items.front()].id; components.push_back(entry.second); for (size_t item : items) images[item].component = components.size() - 1; } std::sort(components.begin(), components.end(), [](const Component &a, const Component &b) { return a.key < b.key; }); std::map component_by_image; for (size_t component = 0; component < components.size(); ++component) for (size_t image : components[component].image_indices) { images[image].component = component; component_by_image[images[image].id] = component; } std::vector output_components; for (const Component &component : components) append_component(component, images, &output_components); std::vector cameras; std::vector landmarks; std::set registered; std::set accepted_tracks; std::map component_keys; for (const Component &component : components) component_keys[component.key] = component.key; for (const Component &component : components) { if (component.image_indices.size() < 2) continue; struct Candidate { uint64_t a; uint64_t b; uint32_t shared; }; std::vector candidates; std::map, uint32_t> shared_by_pair; for (const auto &entry : tracks_by_id) { std::vector track_images; for (const Observation &observation : entry.second.observations) if (std::find(track_images.begin(), track_images.end(), observation.image_id) == track_images.end()) track_images.push_back(observation.image_id); std::sort(track_images.begin(), track_images.end()); for (size_t left = 0; left < track_images.size(); ++left) for (size_t right = left + 1; right < track_images.size(); ++right) if (component_by_image[track_images[left]] == component_by_image[track_images[right]] && component_by_image[track_images[left]] == component_by_image[component.key]) ++shared_by_pair[{track_images[left], track_images[right]}]; } for (const auto &entry : shared_by_pair) if (entry.second >= parameters->minimum_seed_tracks) candidates.push_back({entry.first.first, entry.first.second, entry.second}); std::sort(candidates.begin(), candidates.end(), [](const Candidate &a, const Candidate &b) { if (a.shared != b.shared) return a.shared > b.shared; if (a.a != b.a) return a.a < b.a; return a.b < b.b; }); const size_t candidate_limit = std::min( candidates.size(), parameters->maximum_seed_candidates); result->seed_candidates_available += candidates.size(); bool seeded = false; for (size_t candidate_index = 0; candidate_index < candidate_limit; ++candidate_index) { ++result->seed_candidates_considered; const Candidate &candidate = candidates[candidate_index]; const Image &image_a = images[image_index[candidate.a]]; const Image &image_b = images[image_index[candidate.b]]; std::vector pair_a, pair_b; std::vector track_ids; for (const auto &entry : tracks_by_id) { const Observation *a = nullptr, *b = nullptr; for (const Observation &observation : entry.second.observations) { if (observation.image_id == candidate.a) a = &observation; if (observation.image_id == candidate.b) b = &observation; } if (a && b) { track_ids.push_back(entry.first); pair_a.push_back(*a); pair_b.push_back(*b); } } std::vector pixels_a(pair_a.size()); std::vector pixels_b(pair_b.size()); for (size_t i = 0; i < pair_a.size(); ++i) { pixels_a[i] = pair_a[i].pixel; pixels_b[i] = pair_b[i].pixel; } std::vector mask(pair_a.size()); Lardon3DSparseGeometryRelativePoseResult pose_result = {}; pose_result.inlier_mask = mask.data(); pose_result.inlier_mask_capacity = mask.size(); auto relative = parameters->relative_pose; relative.minimum_inliers = std::max( relative.minimum_inliers, parameters->minimum_seed_tracks); const Lardon3DSparseGeometryResult relative_status = lardon3d_sparse_geometry_relative_pose( &image_a.calibration, &image_b.calibration, pixels_a.data(), pixels_b.data(), pixels_a.size(), &relative, &pose_result); result->last_seed_geometry_status = relative_status; result->last_seed_parallax_rad = pose_result.median_parallax_rad; if (relative_status != LARDON3D_SPARSE_GEOMETRY_OK || pose_result.inlier_count < parameters->minimum_seed_landmarks || pose_result.median_parallax_rad < parameters->minimum_track_parallax_rad || !finite_pose(pose_result.pose_ba)) continue; Pose pose_a = {candidate.a, identity_pose()}; Pose pose_b = {candidate.b, pose_result.pose_ba}; std::vector seed_landmarks; for (size_t i = 0; i < pair_a.size(); ++i) { if (!mask[i]) continue; ++result->triangulation_attempts; Lardon3DSparseGeometryPoint2 normalized[2]; if (lardon3d_sparse_geometry_normalize(&image_a.calibration, &pair_a[i].pixel, 1, &normalized[0]) != LARDON3D_SPARSE_GEOMETRY_OK || lardon3d_sparse_geometry_normalize(&image_b.calibration, &pair_b[i].pixel, 1, &normalized[1]) != LARDON3D_SPARSE_GEOMETRY_OK) continue; Lardon3DSparseGeometryPoint3 point; if (lardon3d_sparse_geometry_triangulate_two_view( &normalized[0], &normalized[1], &pose_a.value, &pose_b.value, &point) != LARDON3D_SPARSE_GEOMETRY_OK) { ++result->triangulation_failures; continue; } Lardon3DSparseGeometryPoint3 refined; Lardon3DSparseGeometryPose seed_poses[2] = {pose_a.value, pose_b.value}; if (lardon3d_sparse_geometry_refine_point( normalized, seed_poses, 2, &point, ¶meters->refinement, &refined) == LARDON3D_SPARSE_GEOMETRY_OK) point = refined; Landmark landmark = {track_ids[i], component.key, point, {pair_a[i], pair_b[i]}, 0.0, 0.0}; const double error_a = reprojection_error(image_a, pose_a, landmark, pair_a[i]); const double error_b = reprojection_error(image_b, pose_b, landmark, pair_b[i]); if (!std::isfinite(error_a) || !std::isfinite(error_b) || std::max(error_a, error_b) > parameters->reprojection_threshold_px) { if (!std::isfinite(error_a) || !std::isfinite(error_b)) ++result->rejected_behind_camera; else ++result->rejected_reprojection; continue; } landmark.rmse = std::sqrt((error_a * error_a + error_b * error_b) / 2.0); landmark.median = std::max(error_a, error_b); seed_landmarks.push_back(landmark); } if (seed_landmarks.size() < parameters->minimum_seed_landmarks) continue; cameras.push_back(pose_a); cameras.push_back(pose_b); registered.insert(candidate.a); registered.insert(candidate.b); for (Landmark &landmark : seed_landmarks) { accepted_tracks.insert(landmark.track_id); landmarks.push_back(landmark); } seeded = true; result->seed_image_a = candidate.a; result->seed_image_b = candidate.b; break; } if (!seeded) continue; bool stopped_without_growth = false; for (uint32_t round = 0; round < parameters->maximum_registration_rounds; ++round) { bool all_component_images_registered = true; for (size_t image_position : component.image_indices) if (!registered.count(images[image_position].id)) { all_component_images_registered = false; break; } if (all_component_images_registered) break; ++result->registration_rounds; struct CandidateImage { uint64_t id; uint32_t support; }; std::vector image_candidates; for (size_t image_position : component.image_indices) { const uint64_t image_id = images[image_position].id; if (registered.count(image_id)) continue; uint32_t support = 0; for (const Landmark &landmark : landmarks) { for (const Observation &observation : tracks_by_id[landmark.track_id].observations) if (observation.image_id == image_id) { ++support; break; } } if (support >= parameters->minimum_pnp_correspondences) image_candidates.push_back({image_id, support}); } std::sort(image_candidates.begin(), image_candidates.end(), [](const CandidateImage &a, const CandidateImage &b) { if (a.support != b.support) return a.support > b.support; return a.id < b.id; }); if (image_candidates.empty()) { stopped_without_growth = true; break; } bool registered_one = false; for (const CandidateImage &candidate : image_candidates) { const Image &image = images[image_index[candidate.id]]; std::vector points; std::vector pixels; std::vector track_ids; for (const Landmark &landmark : landmarks) { const Track &track = tracks_by_id[landmark.track_id]; for (const Observation &observation : track.observations) if (observation.image_id == candidate.id) { points.push_back(landmark.point); pixels.push_back(observation.pixel); track_ids.push_back(landmark.track_id); break; } } std::vector mask(points.size()); Lardon3DSparseGeometryPnPResult pnp_result = {}; pnp_result.inlier_mask = mask.data(); pnp_result.inlier_mask_capacity = mask.size(); ++result->registration_attempts; if (lardon3d_sparse_geometry_pnp( &image.calibration, points.data(), pixels.data(), points.size(), ¶meters->pnp, &pnp_result) != LARDON3D_SPARSE_GEOMETRY_OK || !finite_pose(pnp_result.pose_cw)) { ++result->registration_failures; continue; } ++result->registration_successes; result->last_pnp_inlier_count = pnp_result.inlier_count; Pose pose = {candidate.id, pnp_result.pose_cw}; cameras.push_back(pose); registered.insert(candidate.id); registered_one = true; break; } if (!registered_one) { stopped_without_growth = true; break; } for (Landmark &landmark : landmarks) { if (landmark.component_key != component.key) continue; const Track &track = tracks_by_id[landmark.track_id]; size_t eligible_count = 0; for (const Observation &observation : track.observations) if (registered.count(observation.image_id)) ++eligible_count; if (eligible_count <= landmark.observations.size()) continue; ++result->landmark_update_attempts; ++result->triangulation_attempts; Landmark replacement; Lardon3DSparseGeometryResult geometry_status = LARDON3D_SPARSE_GEOMETRY_OK; const LandmarkCandidateStatus update_status = build_landmark_candidate( track, component.key, images, image_index, cameras, registered, *parameters, &replacement, &geometry_status, result); result->last_triangulation_status = geometry_status; if (update_status == LandmarkCandidateStatus::accepted) { landmark = std::move(replacement); ++result->landmark_update_successes; } else { ++result->landmark_update_failures; record_landmark_rejection(update_status, result); } } size_t added = 0; for (const auto &entry : tracks_by_id) { if (accepted_tracks.count(entry.first) || added >= parameters->maximum_landmarks_per_round) continue; size_t eligible_count = 0; for (const Observation &observation : entry.second.observations) if (registered.count(observation.image_id)) ++eligible_count; if (eligible_count < 2) continue; ++result->triangulation_attempts; Landmark landmark; Lardon3DSparseGeometryResult geometry_status = LARDON3D_SPARSE_GEOMETRY_OK; const LandmarkCandidateStatus candidate_status = build_landmark_candidate( entry.second, component.key, images, image_index, cameras, registered, *parameters, &landmark, &geometry_status, result); result->last_triangulation_status = geometry_status; if (candidate_status != LandmarkCandidateStatus::accepted) { record_landmark_rejection(candidate_status, result); continue; } landmarks.push_back(landmark); accepted_tracks.insert(entry.first); ++added; } } size_t component_registered = 0; for (size_t image_position : component.image_indices) if (registered.count(images[image_position].id)) ++component_registered; if (component_registered < component.image_indices.size()) { if (stopped_without_growth) ++result->no_growth_terminations; else ++result->round_limit_terminations; } } std::sort(cameras.begin(), cameras.end(), [](const Pose &a, const Pose &b) { return a.image_id < b.image_id; }); std::sort(landmarks.begin(), landmarks.end(), [](const Landmark &a, const Landmark &b) { if (a.component_key != b.component_key) return a.component_key < b.component_key; return a.track_id < b.track_id; }); for (const Component &component : components) { for (auto &output : output_components) if (output.component_key == component.key) { for (const Pose &camera : cameras) if (component_by_image[camera.image_id] == component_by_image[component.key]) ++output.registered_image_count; for (const Landmark &landmark : landmarks) if (landmark.component_key == component.key) ++output.landmark_count; } for (size_t image_index_value : component.image_indices) { const uint64_t image_id = images[image_index_value].id; if (!registered.count(image_id)) result->unregistered_image_count++; } } result->component_count = output_components.size(); result->camera_count = cameras.size(); result->landmark_count = landmarks.size(); if (result->component_count) { result->components = static_cast( std::malloc(result->component_count * sizeof(*result->components))); if (!result->components) { destroy_result(result); return LARDON3D_SPARSE_INCREMENTAL_OUT_OF_MEMORY; } std::copy(output_components.begin(), output_components.end(), result->components); } if (result->camera_count) { result->cameras = static_cast( std::malloc(result->camera_count * sizeof(*result->cameras))); if (!result->cameras) { destroy_result(result); return LARDON3D_SPARSE_INCREMENTAL_OUT_OF_MEMORY; } for (size_t i = 0; i < cameras.size(); ++i) result->cameras[i] = { cameras[i].image_id, components[component_by_image[cameras[i].image_id]].key, cameras[i].value}; } if (result->landmark_count) { result->landmarks = static_cast( std::malloc(result->landmark_count * sizeof(*result->landmarks))); if (!result->landmarks) { destroy_result(result); return LARDON3D_SPARSE_INCREMENTAL_OUT_OF_MEMORY; } size_t observation_count = 0; for (const Landmark &landmark : landmarks) observation_count += landmark.observations.size(); result->observation_count = observation_count; result->observations = static_cast( std::malloc(observation_count * sizeof(*result->observations))); if (!result->observations) { destroy_result(result); return LARDON3D_SPARSE_INCREMENTAL_OUT_OF_MEMORY; } size_t observation_offset = 0; for (size_t i = 0; i < landmarks.size(); ++i) { const Landmark &landmark = landmarks[i]; result->landmarks[i] = {landmark.track_id, landmark.track_id, landmark.component_key, landmark.point, landmark.rmse, landmark.median, landmark.observations.size()}; for (size_t position = 0; position < landmark.observations.size(); ++position) { const Observation &observation = landmark.observations[position]; result->observations[observation_offset++] = { landmark.track_id, landmark.track_id, observation.image_id, observation.feature_set_id, observation.feature_index, static_cast(position)}; } } } if (result->unregistered_image_count) { result->unregistered_images = static_cast( std::malloc(result->unregistered_image_count * sizeof(*result->unregistered_images))); if (!result->unregistered_images) { destroy_result(result); return LARDON3D_SPARSE_INCREMENTAL_OUT_OF_MEMORY; } size_t offset = 0; for (const Image &image : images) if (!registered.count(image.id)) result->unregistered_images[offset++] = { image.id, components[component_by_image[image.id]].key}; } result->track_set_id = input->track_set_id; result->calibration_scope_id = input->calibration_scope_id; result->status = result->camera_count == 0 ? LARDON3D_SPARSE_INCREMENTAL_FAILED : result->unregistered_image_count == 0 ? LARDON3D_SPARSE_INCREMENTAL_COMPLETE : LARDON3D_SPARSE_INCREMENTAL_PARTIAL; return result->status; } catch (const std::bad_alloc &) { destroy_result(result); return LARDON3D_SPARSE_INCREMENTAL_OUT_OF_MEMORY; } catch (...) { destroy_result(result); return LARDON3D_SPARSE_INCREMENTAL_FAILED; } } extern "C" void lardon3d_sparse_incremental_result_destroy( Lardon3DSparseIncrementalResult *result) { destroy_result(result); }