Add tracked triangulation association reports
This commit is contained in:
+77
-13
@@ -20,6 +20,7 @@ namespace
|
|||||||
using PoseArray2D =
|
using PoseArray2D =
|
||||||
nb::ndarray<nb::numpy, const float, nb::shape<-1, -1, -1, 3>, nb::c_contig>;
|
nb::ndarray<nb::numpy, const float, nb::shape<-1, -1, -1, 3>, nb::c_contig>;
|
||||||
using CountArray = nb::ndarray<nb::numpy, const uint32_t, nb::shape<-1>, nb::c_contig>;
|
using CountArray = nb::ndarray<nb::numpy, const uint32_t, nb::shape<-1>, nb::c_contig>;
|
||||||
|
using TrackIdArray = nb::ndarray<nb::numpy, const int64_t, nb::shape<-1>, nb::c_contig>;
|
||||||
using PoseArray3DConst =
|
using PoseArray3DConst =
|
||||||
nb::ndarray<nb::numpy, const float, nb::shape<-1, -1, 4>, nb::c_contig>;
|
nb::ndarray<nb::numpy, const float, nb::shape<-1, -1, 4>, nb::c_contig>;
|
||||||
using PoseArray3D = nb::ndarray<nb::numpy, float, nb::shape<-1, -1, 4>, nb::c_contig>;
|
using PoseArray3D = nb::ndarray<nb::numpy, float, nb::shape<-1, -1, 4>, nb::c_contig>;
|
||||||
@@ -58,6 +59,24 @@ PoseBatch3DView pose_batch3d_view_from_numpy(const PoseArray3DConst &poses_3d)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrackedPoseBatch3DView tracked_pose_batch_view_from_numpy(
|
||||||
|
const PoseArray3DConst &poses_3d,
|
||||||
|
const TrackIdArray &track_ids)
|
||||||
|
{
|
||||||
|
if (poses_3d.shape(0) != track_ids.shape(0))
|
||||||
|
{
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"previous_poses_3d and previous_track_ids must have the same number of tracks.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return TrackedPoseBatch3DView {
|
||||||
|
track_ids.data(),
|
||||||
|
poses_3d.data(),
|
||||||
|
static_cast<size_t>(poses_3d.shape(0)),
|
||||||
|
static_cast<size_t>(poses_3d.shape(1)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
PoseArray3D pose_batch_to_numpy(PoseBatch3D batch)
|
PoseArray3D pose_batch_to_numpy(PoseBatch3D batch)
|
||||||
{
|
{
|
||||||
auto *storage = new std::vector<float>(std::move(batch.data));
|
auto *storage = new std::vector<float>(std::move(batch.data));
|
||||||
@@ -216,6 +235,7 @@ NB_MODULE(_core, m)
|
|||||||
nb::class_<PreviousPoseMatch>(m, "PreviousPoseMatch")
|
nb::class_<PreviousPoseMatch>(m, "PreviousPoseMatch")
|
||||||
.def(nb::init<>())
|
.def(nb::init<>())
|
||||||
.def_rw("previous_pose_index", &PreviousPoseMatch::previous_pose_index)
|
.def_rw("previous_pose_index", &PreviousPoseMatch::previous_pose_index)
|
||||||
|
.def_rw("previous_track_id", &PreviousPoseMatch::previous_track_id)
|
||||||
.def_rw("score_view1", &PreviousPoseMatch::score_view1)
|
.def_rw("score_view1", &PreviousPoseMatch::score_view1)
|
||||||
.def_rw("score_view2", &PreviousPoseMatch::score_view2)
|
.def_rw("score_view2", &PreviousPoseMatch::score_view2)
|
||||||
.def_rw("matched_view1", &PreviousPoseMatch::matched_view1)
|
.def_rw("matched_view1", &PreviousPoseMatch::matched_view1)
|
||||||
@@ -274,6 +294,34 @@ NB_MODULE(_core, m)
|
|||||||
return merged_poses_to_numpy_copy(merge.merged_poses);
|
return merged_poses_to_numpy_copy(merge.merged_poses);
|
||||||
}, nb::rv_policy::move);
|
}, nb::rv_policy::move);
|
||||||
|
|
||||||
|
nb::enum_<AssociationStatus>(m, "AssociationStatus")
|
||||||
|
.value("MATCHED", AssociationStatus::Matched)
|
||||||
|
.value("NEW", AssociationStatus::New)
|
||||||
|
.value("AMBIGUOUS", AssociationStatus::Ambiguous);
|
||||||
|
|
||||||
|
nb::class_<AssociationReport>(m, "AssociationReport")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def_rw("pose_previous_indices", &AssociationReport::pose_previous_indices)
|
||||||
|
.def_rw("pose_previous_track_ids", &AssociationReport::pose_previous_track_ids)
|
||||||
|
.def_rw("pose_status", &AssociationReport::pose_status)
|
||||||
|
.def_rw("pose_candidate_previous_indices", &AssociationReport::pose_candidate_previous_indices)
|
||||||
|
.def_rw("pose_candidate_previous_track_ids", &AssociationReport::pose_candidate_previous_track_ids)
|
||||||
|
.def_rw("unmatched_previous_indices", &AssociationReport::unmatched_previous_indices)
|
||||||
|
.def_rw("unmatched_previous_track_ids", &AssociationReport::unmatched_previous_track_ids)
|
||||||
|
.def_rw("new_pose_indices", &AssociationReport::new_pose_indices)
|
||||||
|
.def_rw("ambiguous_pose_indices", &AssociationReport::ambiguous_pose_indices);
|
||||||
|
|
||||||
|
nb::class_<FinalPoseAssociationDebug>(m, "FinalPoseAssociationDebug")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def_rw("final_pose_index", &FinalPoseAssociationDebug::final_pose_index)
|
||||||
|
.def_rw("source_core_proposal_indices", &FinalPoseAssociationDebug::source_core_proposal_indices)
|
||||||
|
.def_rw("source_pair_indices", &FinalPoseAssociationDebug::source_pair_indices)
|
||||||
|
.def_rw("candidate_previous_indices", &FinalPoseAssociationDebug::candidate_previous_indices)
|
||||||
|
.def_rw("candidate_previous_track_ids", &FinalPoseAssociationDebug::candidate_previous_track_ids)
|
||||||
|
.def_rw("resolved_previous_index", &FinalPoseAssociationDebug::resolved_previous_index)
|
||||||
|
.def_rw("resolved_previous_track_id", &FinalPoseAssociationDebug::resolved_previous_track_id)
|
||||||
|
.def_rw("status", &FinalPoseAssociationDebug::status);
|
||||||
|
|
||||||
nb::class_<TriangulationTrace>(m, "TriangulationTrace")
|
nb::class_<TriangulationTrace>(m, "TriangulationTrace")
|
||||||
.def(nb::init<>())
|
.def(nb::init<>())
|
||||||
.def_rw("pairs", &TriangulationTrace::pairs)
|
.def_rw("pairs", &TriangulationTrace::pairs)
|
||||||
@@ -282,11 +330,21 @@ NB_MODULE(_core, m)
|
|||||||
.def_rw("grouping", &TriangulationTrace::grouping)
|
.def_rw("grouping", &TriangulationTrace::grouping)
|
||||||
.def_rw("full_proposals", &TriangulationTrace::full_proposals)
|
.def_rw("full_proposals", &TriangulationTrace::full_proposals)
|
||||||
.def_rw("merge", &TriangulationTrace::merge)
|
.def_rw("merge", &TriangulationTrace::merge)
|
||||||
|
.def_rw("association", &TriangulationTrace::association)
|
||||||
|
.def_rw("final_pose_associations", &TriangulationTrace::final_pose_associations)
|
||||||
.def_prop_ro("final_poses", [](const TriangulationTrace &trace)
|
.def_prop_ro("final_poses", [](const TriangulationTrace &trace)
|
||||||
{
|
{
|
||||||
return pose_batch_to_numpy_copy(trace.final_poses);
|
return pose_batch_to_numpy_copy(trace.final_poses);
|
||||||
}, nb::rv_policy::move);
|
}, nb::rv_policy::move);
|
||||||
|
|
||||||
|
nb::class_<TriangulationResult>(m, "TriangulationResult")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def_rw("association", &TriangulationResult::association)
|
||||||
|
.def_prop_ro("poses_3d", [](const TriangulationResult &result)
|
||||||
|
{
|
||||||
|
return pose_batch_to_numpy_copy(result.poses);
|
||||||
|
}, nb::rv_policy::move);
|
||||||
|
|
||||||
m.def(
|
m.def(
|
||||||
"make_camera",
|
"make_camera",
|
||||||
&make_camera,
|
&make_camera,
|
||||||
@@ -313,17 +371,19 @@ NB_MODULE(_core, m)
|
|||||||
[](const PoseArray2D &poses_2d,
|
[](const PoseArray2D &poses_2d,
|
||||||
const CountArray &person_counts,
|
const CountArray &person_counts,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseArray3DConst &previous_poses_3d)
|
const PoseArray3DConst &previous_poses_3d,
|
||||||
|
const TrackIdArray &previous_track_ids)
|
||||||
{
|
{
|
||||||
return filter_pairs_with_previous_poses(
|
return filter_pairs_with_previous_poses(
|
||||||
pose_batch_view_from_numpy(poses_2d, person_counts),
|
pose_batch_view_from_numpy(poses_2d, person_counts),
|
||||||
config,
|
config,
|
||||||
pose_batch3d_view_from_numpy(previous_poses_3d));
|
tracked_pose_batch_view_from_numpy(previous_poses_3d, previous_track_ids));
|
||||||
},
|
},
|
||||||
"poses_2d"_a,
|
"poses_2d"_a,
|
||||||
"person_counts"_a,
|
"person_counts"_a,
|
||||||
"config"_a,
|
"config"_a,
|
||||||
"previous_poses_3d"_a);
|
"previous_poses_3d"_a,
|
||||||
|
"previous_track_ids"_a);
|
||||||
|
|
||||||
m.def(
|
m.def(
|
||||||
"triangulate_debug",
|
"triangulate_debug",
|
||||||
@@ -342,9 +402,11 @@ NB_MODULE(_core, m)
|
|||||||
[](const PoseArray2D &poses_2d,
|
[](const PoseArray2D &poses_2d,
|
||||||
const CountArray &person_counts,
|
const CountArray &person_counts,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseArray3DConst &previous_poses_3d)
|
const PoseArray3DConst &previous_poses_3d,
|
||||||
|
const TrackIdArray &previous_track_ids)
|
||||||
{
|
{
|
||||||
const PoseBatch3DView previous_view = pose_batch3d_view_from_numpy(previous_poses_3d);
|
const TrackedPoseBatch3DView previous_view =
|
||||||
|
tracked_pose_batch_view_from_numpy(previous_poses_3d, previous_track_ids);
|
||||||
return triangulate_debug(
|
return triangulate_debug(
|
||||||
pose_batch_view_from_numpy(poses_2d, person_counts),
|
pose_batch_view_from_numpy(poses_2d, person_counts),
|
||||||
config,
|
config,
|
||||||
@@ -353,7 +415,8 @@ NB_MODULE(_core, m)
|
|||||||
"poses_2d"_a,
|
"poses_2d"_a,
|
||||||
"person_counts"_a,
|
"person_counts"_a,
|
||||||
"config"_a,
|
"config"_a,
|
||||||
"previous_poses_3d"_a);
|
"previous_poses_3d"_a,
|
||||||
|
"previous_track_ids"_a);
|
||||||
|
|
||||||
m.def(
|
m.def(
|
||||||
"triangulate_poses",
|
"triangulate_poses",
|
||||||
@@ -370,21 +433,22 @@ NB_MODULE(_core, m)
|
|||||||
"config"_a);
|
"config"_a);
|
||||||
|
|
||||||
m.def(
|
m.def(
|
||||||
"triangulate_poses",
|
"triangulate_with_report",
|
||||||
[](const PoseArray2D &poses_2d,
|
[](const PoseArray2D &poses_2d,
|
||||||
const CountArray &person_counts,
|
const CountArray &person_counts,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseArray3DConst &previous_poses_3d)
|
const PoseArray3DConst &previous_poses_3d,
|
||||||
|
const TrackIdArray &previous_track_ids)
|
||||||
{
|
{
|
||||||
const PoseBatch3DView previous_view = pose_batch3d_view_from_numpy(previous_poses_3d);
|
const TriangulationResult result = triangulate_with_report(
|
||||||
const PoseBatch3D poses_3d = triangulate_poses(
|
|
||||||
pose_batch_view_from_numpy(poses_2d, person_counts),
|
pose_batch_view_from_numpy(poses_2d, person_counts),
|
||||||
config,
|
config,
|
||||||
&previous_view);
|
tracked_pose_batch_view_from_numpy(previous_poses_3d, previous_track_ids));
|
||||||
return pose_batch_to_numpy(poses_3d);
|
return result;
|
||||||
},
|
},
|
||||||
"poses_2d"_a,
|
"poses_2d"_a,
|
||||||
"person_counts"_a,
|
"person_counts"_a,
|
||||||
"config"_a,
|
"config"_a,
|
||||||
"previous_poses_3d"_a);
|
"previous_poses_3d"_a,
|
||||||
|
"previous_track_ids"_a);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,16 @@ const float &PoseBatch3DView::at(size_t person, size_t joint, size_t coord) cons
|
|||||||
return data[pose3d_offset(person, joint, coord, num_joints)];
|
return data[pose3d_offset(person, joint, coord, num_joints)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t TrackedPoseBatch3DView::track_id(size_t person) const
|
||||||
|
{
|
||||||
|
return track_ids[person];
|
||||||
|
}
|
||||||
|
|
||||||
|
const float &TrackedPoseBatch3DView::at(size_t person, size_t joint, size_t coord) const
|
||||||
|
{
|
||||||
|
return data[pose3d_offset(person, joint, coord, num_joints)];
|
||||||
|
}
|
||||||
|
|
||||||
const float &PoseBatch2D::at(size_t view, size_t person, size_t joint, size_t coord) const
|
const float &PoseBatch2D::at(size_t view, size_t person, size_t joint, size_t coord) const
|
||||||
{
|
{
|
||||||
return data[pose2d_offset(view, person, joint, coord, max_persons, num_joints)];
|
return data[pose2d_offset(view, person, joint, coord, max_persons, num_joints)];
|
||||||
|
|||||||
+78
-37
@@ -34,6 +34,17 @@ struct PoseBatch3DView
|
|||||||
const float &at(size_t person, size_t joint, size_t coord) const;
|
const float &at(size_t person, size_t joint, size_t coord) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TrackedPoseBatch3DView
|
||||||
|
{
|
||||||
|
const int64_t *track_ids = nullptr;
|
||||||
|
const float *data = nullptr;
|
||||||
|
size_t num_persons = 0;
|
||||||
|
size_t num_joints = 0;
|
||||||
|
|
||||||
|
int64_t track_id(size_t person) const;
|
||||||
|
const float &at(size_t person, size_t joint, size_t coord) const;
|
||||||
|
};
|
||||||
|
|
||||||
struct PoseBatch2D
|
struct PoseBatch2D
|
||||||
{
|
{
|
||||||
std::vector<float> data;
|
std::vector<float> data;
|
||||||
@@ -78,6 +89,7 @@ struct PairCandidate
|
|||||||
struct PreviousPoseMatch
|
struct PreviousPoseMatch
|
||||||
{
|
{
|
||||||
int previous_pose_index = -1;
|
int previous_pose_index = -1;
|
||||||
|
int64_t previous_track_id = -1;
|
||||||
float score_view1 = 0.0f;
|
float score_view1 = 0.0f;
|
||||||
float score_view2 = 0.0f;
|
float score_view2 = 0.0f;
|
||||||
bool matched_view1 = false;
|
bool matched_view1 = false;
|
||||||
@@ -131,6 +143,38 @@ struct MergeDebug
|
|||||||
std::vector<std::vector<int>> group_proposal_indices;
|
std::vector<std::vector<int>> group_proposal_indices;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class AssociationStatus
|
||||||
|
{
|
||||||
|
Matched,
|
||||||
|
New,
|
||||||
|
Ambiguous,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssociationReport
|
||||||
|
{
|
||||||
|
std::vector<int> pose_previous_indices;
|
||||||
|
std::vector<int64_t> pose_previous_track_ids;
|
||||||
|
std::vector<AssociationStatus> pose_status;
|
||||||
|
std::vector<std::vector<int>> pose_candidate_previous_indices;
|
||||||
|
std::vector<std::vector<int64_t>> pose_candidate_previous_track_ids;
|
||||||
|
std::vector<int> unmatched_previous_indices;
|
||||||
|
std::vector<int64_t> unmatched_previous_track_ids;
|
||||||
|
std::vector<int> new_pose_indices;
|
||||||
|
std::vector<int> ambiguous_pose_indices;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FinalPoseAssociationDebug
|
||||||
|
{
|
||||||
|
int final_pose_index = -1;
|
||||||
|
std::vector<int> source_core_proposal_indices;
|
||||||
|
std::vector<int> source_pair_indices;
|
||||||
|
std::vector<int> candidate_previous_indices;
|
||||||
|
std::vector<int64_t> candidate_previous_track_ids;
|
||||||
|
int resolved_previous_index = -1;
|
||||||
|
int64_t resolved_previous_track_id = -1;
|
||||||
|
AssociationStatus status = AssociationStatus::New;
|
||||||
|
};
|
||||||
|
|
||||||
struct TriangulationTrace
|
struct TriangulationTrace
|
||||||
{
|
{
|
||||||
std::vector<PairCandidate> pairs;
|
std::vector<PairCandidate> pairs;
|
||||||
@@ -139,9 +183,17 @@ struct TriangulationTrace
|
|||||||
GroupingDebug grouping;
|
GroupingDebug grouping;
|
||||||
std::vector<FullProposalDebug> full_proposals;
|
std::vector<FullProposalDebug> full_proposals;
|
||||||
MergeDebug merge;
|
MergeDebug merge;
|
||||||
|
AssociationReport association;
|
||||||
|
std::vector<FinalPoseAssociationDebug> final_pose_associations;
|
||||||
PoseBatch3D final_poses;
|
PoseBatch3D final_poses;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TriangulationResult
|
||||||
|
{
|
||||||
|
PoseBatch3D poses;
|
||||||
|
AssociationReport association;
|
||||||
|
};
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
struct TriangulationOptions
|
struct TriangulationOptions
|
||||||
@@ -165,40 +217,15 @@ std::vector<PairCandidate> build_pair_candidates(const PoseBatch2DView &poses_2d
|
|||||||
PreviousPoseFilterDebug filter_pairs_with_previous_poses(
|
PreviousPoseFilterDebug filter_pairs_with_previous_poses(
|
||||||
const PoseBatch2DView &poses_2d,
|
const PoseBatch2DView &poses_2d,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseBatch3DView &previous_poses_3d,
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
const TriangulationOptions *options_override = nullptr);
|
const TriangulationOptions *options_override = nullptr);
|
||||||
|
|
||||||
inline PreviousPoseFilterDebug filter_pairs_with_previous_poses(
|
|
||||||
const PoseBatch2D &poses_2d,
|
|
||||||
const TriangulationConfig &config,
|
|
||||||
const PoseBatch3D &previous_poses_3d,
|
|
||||||
const TriangulationOptions *options_override = nullptr)
|
|
||||||
{
|
|
||||||
return filter_pairs_with_previous_poses(poses_2d.view(), config, previous_poses_3d.view(), options_override);
|
|
||||||
}
|
|
||||||
|
|
||||||
TriangulationTrace triangulate_debug(
|
TriangulationTrace triangulate_debug(
|
||||||
const PoseBatch2DView &poses_2d,
|
const PoseBatch2DView &poses_2d,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseBatch3DView *previous_poses_3d = nullptr,
|
const TrackedPoseBatch3DView *previous_poses_3d = nullptr,
|
||||||
const TriangulationOptions *options_override = nullptr);
|
const TriangulationOptions *options_override = nullptr);
|
||||||
|
|
||||||
inline TriangulationTrace triangulate_debug(
|
|
||||||
const PoseBatch2D &poses_2d,
|
|
||||||
const TriangulationConfig &config,
|
|
||||||
const PoseBatch3D *previous_poses_3d = nullptr,
|
|
||||||
const TriangulationOptions *options_override = nullptr)
|
|
||||||
{
|
|
||||||
PoseBatch3DView previous_view_storage;
|
|
||||||
const PoseBatch3DView *previous_view = nullptr;
|
|
||||||
if (previous_poses_3d != nullptr)
|
|
||||||
{
|
|
||||||
previous_view_storage = previous_poses_3d->view();
|
|
||||||
previous_view = &previous_view_storage;
|
|
||||||
}
|
|
||||||
return triangulate_debug(poses_2d.view(), config, previous_view, options_override);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,21 +240,35 @@ inline TriangulationTrace triangulate_debug(
|
|||||||
PoseBatch3D triangulate_poses(
|
PoseBatch3D triangulate_poses(
|
||||||
const PoseBatch2DView &poses_2d,
|
const PoseBatch2DView &poses_2d,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseBatch3DView *previous_poses_3d = nullptr,
|
const TriangulationOptions *options_override = nullptr);
|
||||||
|
|
||||||
|
TriangulationResult triangulate_with_report(
|
||||||
|
const PoseBatch2DView &poses_2d,
|
||||||
|
const TriangulationConfig &config,
|
||||||
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
const TriangulationOptions *options_override = nullptr);
|
const TriangulationOptions *options_override = nullptr);
|
||||||
|
|
||||||
inline PoseBatch3D triangulate_poses(
|
inline PoseBatch3D triangulate_poses(
|
||||||
const PoseBatch2D &poses_2d,
|
const PoseBatch2D &poses_2d,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseBatch3D *previous_poses_3d = nullptr,
|
|
||||||
const TriangulationOptions *options_override = nullptr)
|
const TriangulationOptions *options_override = nullptr)
|
||||||
{
|
{
|
||||||
PoseBatch3DView previous_view_storage;
|
return triangulate_poses(poses_2d.view(), config, options_override);
|
||||||
const PoseBatch3DView *previous_view = nullptr;
|
}
|
||||||
if (previous_poses_3d != nullptr)
|
|
||||||
{
|
inline TriangulationTrace triangulate_debug(
|
||||||
previous_view_storage = previous_poses_3d->view();
|
const PoseBatch2D &poses_2d,
|
||||||
previous_view = &previous_view_storage;
|
const TriangulationConfig &config,
|
||||||
}
|
const TriangulationOptions *options_override = nullptr)
|
||||||
return triangulate_poses(poses_2d.view(), config, previous_view, options_override);
|
{
|
||||||
|
return triangulate_debug(poses_2d.view(), config, nullptr, options_override);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline TriangulationResult triangulate_with_report(
|
||||||
|
const PoseBatch2D &poses_2d,
|
||||||
|
const TriangulationConfig &config,
|
||||||
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
|
const TriangulationOptions *options_override = nullptr)
|
||||||
|
{
|
||||||
|
return triangulate_with_report(poses_2d.view(), config, previous_poses_3d, options_override);
|
||||||
}
|
}
|
||||||
|
|||||||
+200
-19
@@ -9,6 +9,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include "interface.hpp"
|
#include "interface.hpp"
|
||||||
|
|
||||||
@@ -190,6 +191,15 @@ struct PreviousProjectionCache
|
|||||||
std::vector<Pose3D> core_poses;
|
std::vector<Pose3D> core_poses;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FinalAssociationState
|
||||||
|
{
|
||||||
|
std::vector<int> candidate_previous_indices;
|
||||||
|
std::vector<int64_t> candidate_previous_track_ids;
|
||||||
|
int resolved_previous_index = -1;
|
||||||
|
int64_t resolved_previous_track_id = -1;
|
||||||
|
AssociationStatus status = AssociationStatus::New;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr std::array<std::string_view, 12> kCoreJoints = {
|
constexpr std::array<std::string_view, 12> kCoreJoints = {
|
||||||
"shoulder_left",
|
"shoulder_left",
|
||||||
"shoulder_right",
|
"shoulder_right",
|
||||||
@@ -218,7 +228,7 @@ constexpr std::array<std::pair<std::string_view, std::string_view>, 8> kCoreLimb
|
|||||||
|
|
||||||
std::vector<PairCandidate> build_pair_candidates_from_packed(const PackedPoseStore2D &packed_poses);
|
std::vector<PairCandidate> build_pair_candidates_from_packed(const PackedPoseStore2D &packed_poses);
|
||||||
PreviousProjectionCache project_previous_poses(
|
PreviousProjectionCache project_previous_poses(
|
||||||
const PoseBatch3DView &previous_poses_3d,
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
const std::vector<Camera> &internal_cameras,
|
const std::vector<Camera> &internal_cameras,
|
||||||
const std::vector<size_t> &core_joint_idx);
|
const std::vector<size_t> &core_joint_idx);
|
||||||
PreviousPoseFilterDebug filter_pairs_with_previous_poses_impl(
|
PreviousPoseFilterDebug filter_pairs_with_previous_poses_impl(
|
||||||
@@ -227,7 +237,7 @@ PreviousPoseFilterDebug filter_pairs_with_previous_poses_impl(
|
|||||||
const std::vector<size_t> &core_joint_idx,
|
const std::vector<size_t> &core_joint_idx,
|
||||||
const std::vector<PairCandidate> &pairs,
|
const std::vector<PairCandidate> &pairs,
|
||||||
const TriangulationOptions &options,
|
const TriangulationOptions &options,
|
||||||
const PoseBatch3DView &previous_poses_3d);
|
const TrackedPoseBatch3DView &previous_poses_3d);
|
||||||
float calc_pose_score(
|
float calc_pose_score(
|
||||||
const Pose2D &pose,
|
const Pose2D &pose,
|
||||||
const Pose2D &reference_pose,
|
const Pose2D &reference_pose,
|
||||||
@@ -279,7 +289,7 @@ TriangulationTrace triangulate_debug_impl(
|
|||||||
const std::vector<Camera> &cameras,
|
const std::vector<Camera> &cameras,
|
||||||
const std::array<std::array<float, 3>, 2> &roomparams,
|
const std::array<std::array<float, 3>, 2> &roomparams,
|
||||||
const std::vector<std::string> &joint_names,
|
const std::vector<std::string> &joint_names,
|
||||||
const PoseBatch3DView *previous_poses_3d,
|
const TrackedPoseBatch3DView *previous_poses_3d,
|
||||||
const TriangulationOptions &options);
|
const TriangulationOptions &options);
|
||||||
|
|
||||||
PreparedInputs prepare_inputs(
|
PreparedInputs prepare_inputs(
|
||||||
@@ -346,6 +356,39 @@ PreparedInputs prepare_inputs(
|
|||||||
std::move(packed_poses));
|
std::move(packed_poses));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void validate_previous_tracks(
|
||||||
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
|
const std::vector<std::string> &joint_names)
|
||||||
|
{
|
||||||
|
if (previous_poses_3d.num_persons == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (previous_poses_3d.track_ids == nullptr)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("previous_track_ids must not be null.");
|
||||||
|
}
|
||||||
|
if (previous_poses_3d.data == nullptr)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("previous_poses_3d data must not be null.");
|
||||||
|
}
|
||||||
|
if (previous_poses_3d.num_joints != joint_names.size())
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("previous_poses_3d must use the same joint count as joint_names.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<int64_t> seen_track_ids;
|
||||||
|
seen_track_ids.reserve(previous_poses_3d.num_persons);
|
||||||
|
for (size_t person = 0; person < previous_poses_3d.num_persons; ++person)
|
||||||
|
{
|
||||||
|
const int64_t track_id = previous_poses_3d.track_id(person);
|
||||||
|
if (!seen_track_ids.insert(track_id).second)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("previous_track_ids must be unique.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<PairCandidate> build_pair_candidates_from_packed(const PackedPoseStore2D &packed_poses)
|
std::vector<PairCandidate> build_pair_candidates_from_packed(const PackedPoseStore2D &packed_poses)
|
||||||
{
|
{
|
||||||
std::vector<PairCandidate> pairs;
|
std::vector<PairCandidate> pairs;
|
||||||
@@ -373,7 +416,7 @@ std::vector<PairCandidate> build_pair_candidates_from_packed(const PackedPoseSto
|
|||||||
}
|
}
|
||||||
|
|
||||||
PreviousProjectionCache project_previous_poses(
|
PreviousProjectionCache project_previous_poses(
|
||||||
const PoseBatch3DView &previous_poses_3d,
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
const std::vector<Camera> &internal_cameras,
|
const std::vector<Camera> &internal_cameras,
|
||||||
const std::vector<size_t> &core_joint_idx)
|
const std::vector<size_t> &core_joint_idx)
|
||||||
{
|
{
|
||||||
@@ -449,7 +492,7 @@ PreviousPoseFilterDebug filter_pairs_with_previous_poses_impl(
|
|||||||
const std::vector<size_t> &core_joint_idx,
|
const std::vector<size_t> &core_joint_idx,
|
||||||
const std::vector<PairCandidate> &pairs,
|
const std::vector<PairCandidate> &pairs,
|
||||||
const TriangulationOptions &options,
|
const TriangulationOptions &options,
|
||||||
const PoseBatch3DView &previous_poses_3d)
|
const TrackedPoseBatch3DView &previous_poses_3d)
|
||||||
{
|
{
|
||||||
PreviousPoseFilterDebug debug;
|
PreviousPoseFilterDebug debug;
|
||||||
debug.used_previous_poses = true;
|
debug.used_previous_poses = true;
|
||||||
@@ -500,6 +543,7 @@ PreviousPoseFilterDebug filter_pairs_with_previous_poses_impl(
|
|||||||
{
|
{
|
||||||
best_match = PreviousPoseMatch {
|
best_match = PreviousPoseMatch {
|
||||||
static_cast<int>(previous_index),
|
static_cast<int>(previous_index),
|
||||||
|
previous_poses_3d.track_id(previous_index),
|
||||||
score_view1,
|
score_view1,
|
||||||
score_view2,
|
score_view2,
|
||||||
true,
|
true,
|
||||||
@@ -515,6 +559,7 @@ PreviousPoseFilterDebug filter_pairs_with_previous_poses_impl(
|
|||||||
{
|
{
|
||||||
best_match = PreviousPoseMatch {
|
best_match = PreviousPoseMatch {
|
||||||
static_cast<int>(previous_index),
|
static_cast<int>(previous_index),
|
||||||
|
previous_poses_3d.track_id(previous_index),
|
||||||
score_view1,
|
score_view1,
|
||||||
score_view2,
|
score_view2,
|
||||||
matched_view1,
|
matched_view1,
|
||||||
@@ -549,16 +594,15 @@ TriangulationTrace triangulate_debug_impl(
|
|||||||
const std::vector<Camera> &cameras,
|
const std::vector<Camera> &cameras,
|
||||||
const std::array<std::array<float, 3>, 2> &roomparams,
|
const std::array<std::array<float, 3>, 2> &roomparams,
|
||||||
const std::vector<std::string> &joint_names,
|
const std::vector<std::string> &joint_names,
|
||||||
const PoseBatch3DView *previous_poses_3d,
|
const TrackedPoseBatch3DView *previous_poses_3d,
|
||||||
const TriangulationOptions &options)
|
const TriangulationOptions &options)
|
||||||
{
|
{
|
||||||
TriangulationTrace trace;
|
TriangulationTrace trace;
|
||||||
trace.final_poses.num_joints = joint_names.size();
|
trace.final_poses.num_joints = joint_names.size();
|
||||||
|
|
||||||
if (previous_poses_3d != nullptr && previous_poses_3d->num_persons > 0 &&
|
if (previous_poses_3d != nullptr)
|
||||||
previous_poses_3d->num_joints != joint_names.size())
|
|
||||||
{
|
{
|
||||||
throw std::invalid_argument("previous_poses_3d must use the same joint count as joint_names.");
|
validate_previous_tracks(*previous_poses_3d, joint_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedInputs prepared = prepare_inputs(poses_2d, cameras, joint_names);
|
PreparedInputs prepared = prepare_inputs(poses_2d, cameras, joint_names);
|
||||||
@@ -831,6 +875,63 @@ TriangulationTrace triangulate_debug_impl(
|
|||||||
trace.merge.merged_poses.push_back(final_poses_3d[i]);
|
trace.merge.merged_poses.push_back(final_poses_3d[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<FinalAssociationState> group_associations(groups.size());
|
||||||
|
if (previous_poses_3d != nullptr)
|
||||||
|
{
|
||||||
|
for (size_t group_index = 0; group_index < trace.merge.group_proposal_indices.size(); ++group_index)
|
||||||
|
{
|
||||||
|
FinalAssociationState association;
|
||||||
|
std::set<int> candidate_previous_indices;
|
||||||
|
std::set<int64_t> candidate_previous_track_ids;
|
||||||
|
|
||||||
|
for (const int core_index : trace.merge.group_proposal_indices[group_index])
|
||||||
|
{
|
||||||
|
if (core_index < 0 || static_cast<size_t>(core_index) >= trace.core_proposals.size())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int pair_index = trace.core_proposals[static_cast<size_t>(core_index)].pair_index;
|
||||||
|
if (pair_index < 0 || static_cast<size_t>(pair_index) >= trace.previous_filter.matches.size())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreviousPoseMatch &match = trace.previous_filter.matches[static_cast<size_t>(pair_index)];
|
||||||
|
if (!match.kept || match.previous_pose_index < 0 || match.previous_track_id < 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate_previous_indices.insert(match.previous_pose_index);
|
||||||
|
candidate_previous_track_ids.insert(match.previous_track_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
association.candidate_previous_indices.assign(
|
||||||
|
candidate_previous_indices.begin(), candidate_previous_indices.end());
|
||||||
|
association.candidate_previous_track_ids.assign(
|
||||||
|
candidate_previous_track_ids.begin(), candidate_previous_track_ids.end());
|
||||||
|
|
||||||
|
if (association.candidate_previous_track_ids.empty())
|
||||||
|
{
|
||||||
|
association.status = AssociationStatus::New;
|
||||||
|
}
|
||||||
|
else if (association.candidate_previous_track_ids.size() == 1 &&
|
||||||
|
association.candidate_previous_indices.size() == 1)
|
||||||
|
{
|
||||||
|
association.status = AssociationStatus::Matched;
|
||||||
|
association.resolved_previous_index = association.candidate_previous_indices.front();
|
||||||
|
association.resolved_previous_track_id = association.candidate_previous_track_ids.front();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
association.status = AssociationStatus::Ambiguous;
|
||||||
|
}
|
||||||
|
|
||||||
|
group_associations[group_index] = std::move(association);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
add_extra_joints(final_poses_3d, joint_names);
|
add_extra_joints(final_poses_3d, joint_names);
|
||||||
filter_poses(final_poses_3d, roomparams, prepared.core_joint_idx, prepared.core_limbs_idx);
|
filter_poses(final_poses_3d, roomparams, prepared.core_joint_idx, prepared.core_limbs_idx);
|
||||||
add_missing_joints(final_poses_3d, joint_names, options.min_match_score);
|
add_missing_joints(final_poses_3d, joint_names, options.min_match_score);
|
||||||
@@ -854,10 +955,20 @@ TriangulationTrace triangulate_debug_impl(
|
|||||||
|
|
||||||
trace.final_poses.num_persons = valid_persons;
|
trace.final_poses.num_persons = valid_persons;
|
||||||
trace.final_poses.data.resize(valid_persons * trace.final_poses.num_joints * 4);
|
trace.final_poses.data.resize(valid_persons * trace.final_poses.num_joints * 4);
|
||||||
|
trace.association.pose_previous_indices.reserve(valid_persons);
|
||||||
|
trace.association.pose_previous_track_ids.reserve(valid_persons);
|
||||||
|
trace.association.pose_status.reserve(valid_persons);
|
||||||
|
trace.association.pose_candidate_previous_indices.reserve(valid_persons);
|
||||||
|
trace.association.pose_candidate_previous_track_ids.reserve(valid_persons);
|
||||||
|
trace.final_pose_associations.reserve(valid_persons);
|
||||||
|
|
||||||
|
std::set<int> resolved_previous_indices;
|
||||||
|
std::set<int64_t> resolved_previous_track_ids;
|
||||||
|
|
||||||
size_t person_index = 0;
|
size_t person_index = 0;
|
||||||
for (const auto &pose : final_poses_3d)
|
for (size_t group_index = 0; group_index < final_poses_3d.size(); ++group_index)
|
||||||
{
|
{
|
||||||
|
const auto &pose = final_poses_3d[group_index];
|
||||||
const bool is_valid = std::any_of(
|
const bool is_valid = std::any_of(
|
||||||
pose.begin(),
|
pose.begin(),
|
||||||
pose.end(),
|
pose.end(),
|
||||||
@@ -877,9 +988,70 @@ TriangulationTrace triangulate_debug_impl(
|
|||||||
trace.final_poses.at(person_index, joint, coord) = pose[joint][coord];
|
trace.final_poses.at(person_index, joint, coord) = pose[joint][coord];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previous_poses_3d != nullptr)
|
||||||
|
{
|
||||||
|
const FinalAssociationState &association = group_associations[group_index];
|
||||||
|
trace.association.pose_previous_indices.push_back(association.resolved_previous_index);
|
||||||
|
trace.association.pose_previous_track_ids.push_back(association.resolved_previous_track_id);
|
||||||
|
trace.association.pose_status.push_back(association.status);
|
||||||
|
trace.association.pose_candidate_previous_indices.push_back(association.candidate_previous_indices);
|
||||||
|
trace.association.pose_candidate_previous_track_ids.push_back(
|
||||||
|
association.candidate_previous_track_ids);
|
||||||
|
|
||||||
|
if (association.status == AssociationStatus::Matched)
|
||||||
|
{
|
||||||
|
resolved_previous_indices.insert(association.resolved_previous_index);
|
||||||
|
resolved_previous_track_ids.insert(association.resolved_previous_track_id);
|
||||||
|
}
|
||||||
|
else if (association.status == AssociationStatus::New)
|
||||||
|
{
|
||||||
|
trace.association.new_pose_indices.push_back(static_cast<int>(person_index));
|
||||||
|
}
|
||||||
|
else if (association.status == AssociationStatus::Ambiguous)
|
||||||
|
{
|
||||||
|
trace.association.ambiguous_pose_indices.push_back(static_cast<int>(person_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
FinalPoseAssociationDebug debug_association;
|
||||||
|
debug_association.final_pose_index = static_cast<int>(person_index);
|
||||||
|
debug_association.source_core_proposal_indices =
|
||||||
|
trace.merge.group_proposal_indices[group_index];
|
||||||
|
debug_association.candidate_previous_indices = association.candidate_previous_indices;
|
||||||
|
debug_association.candidate_previous_track_ids = association.candidate_previous_track_ids;
|
||||||
|
debug_association.resolved_previous_index = association.resolved_previous_index;
|
||||||
|
debug_association.resolved_previous_track_id = association.resolved_previous_track_id;
|
||||||
|
debug_association.status = association.status;
|
||||||
|
debug_association.source_pair_indices.reserve(debug_association.source_core_proposal_indices.size());
|
||||||
|
for (const int core_index : debug_association.source_core_proposal_indices)
|
||||||
|
{
|
||||||
|
if (core_index >= 0 && static_cast<size_t>(core_index) < trace.core_proposals.size())
|
||||||
|
{
|
||||||
|
debug_association.source_pair_indices.push_back(
|
||||||
|
trace.core_proposals[static_cast<size_t>(core_index)].pair_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace.final_pose_associations.push_back(std::move(debug_association));
|
||||||
|
}
|
||||||
|
|
||||||
++person_index;
|
++person_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previous_poses_3d != nullptr)
|
||||||
|
{
|
||||||
|
for (size_t previous_index = 0; previous_index < previous_poses_3d->num_persons; ++previous_index)
|
||||||
|
{
|
||||||
|
const int previous_index_int = static_cast<int>(previous_index);
|
||||||
|
const int64_t track_id = previous_poses_3d->track_id(previous_index);
|
||||||
|
if (!resolved_previous_indices.contains(previous_index_int) &&
|
||||||
|
!resolved_previous_track_ids.contains(track_id))
|
||||||
|
{
|
||||||
|
trace.association.unmatched_previous_indices.push_back(previous_index_int);
|
||||||
|
trace.association.unmatched_previous_track_ids.push_back(track_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return trace;
|
return trace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,13 +1059,10 @@ PreviousPoseFilterDebug filter_pairs_with_previous_poses_impl(
|
|||||||
const PoseBatch2DView &poses_2d,
|
const PoseBatch2DView &poses_2d,
|
||||||
const std::vector<Camera> &cameras,
|
const std::vector<Camera> &cameras,
|
||||||
const std::vector<std::string> &joint_names,
|
const std::vector<std::string> &joint_names,
|
||||||
const PoseBatch3DView &previous_poses_3d,
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
const TriangulationOptions &options)
|
const TriangulationOptions &options)
|
||||||
{
|
{
|
||||||
if (previous_poses_3d.num_persons > 0 && previous_poses_3d.num_joints != joint_names.size())
|
validate_previous_tracks(previous_poses_3d, joint_names);
|
||||||
{
|
|
||||||
throw std::invalid_argument("previous_poses_3d must use the same joint count as joint_names.");
|
|
||||||
}
|
|
||||||
|
|
||||||
PreparedInputs prepared = prepare_inputs(poses_2d, cameras, joint_names);
|
PreparedInputs prepared = prepare_inputs(poses_2d, cameras, joint_names);
|
||||||
const std::vector<PairCandidate> pairs = build_pair_candidates_from_packed(prepared.packed_poses);
|
const std::vector<PairCandidate> pairs = build_pair_candidates_from_packed(prepared.packed_poses);
|
||||||
@@ -2350,7 +2519,7 @@ std::vector<PairCandidate> build_pair_candidates(const PoseBatch2DView &poses_2d
|
|||||||
PreviousPoseFilterDebug filter_pairs_with_previous_poses(
|
PreviousPoseFilterDebug filter_pairs_with_previous_poses(
|
||||||
const PoseBatch2DView &poses_2d,
|
const PoseBatch2DView &poses_2d,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseBatch3DView &previous_poses_3d,
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
const TriangulationOptions *options_override)
|
const TriangulationOptions *options_override)
|
||||||
{
|
{
|
||||||
const TriangulationOptions &options =
|
const TriangulationOptions &options =
|
||||||
@@ -2362,7 +2531,7 @@ PreviousPoseFilterDebug filter_pairs_with_previous_poses(
|
|||||||
TriangulationTrace triangulate_debug(
|
TriangulationTrace triangulate_debug(
|
||||||
const PoseBatch2DView &poses_2d,
|
const PoseBatch2DView &poses_2d,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseBatch3DView *previous_poses_3d,
|
const TrackedPoseBatch3DView *previous_poses_3d,
|
||||||
const TriangulationOptions *options_override)
|
const TriangulationOptions *options_override)
|
||||||
{
|
{
|
||||||
const TriangulationOptions &options =
|
const TriangulationOptions &options =
|
||||||
@@ -2374,8 +2543,20 @@ TriangulationTrace triangulate_debug(
|
|||||||
PoseBatch3D triangulate_poses(
|
PoseBatch3D triangulate_poses(
|
||||||
const PoseBatch2DView &poses_2d,
|
const PoseBatch2DView &poses_2d,
|
||||||
const TriangulationConfig &config,
|
const TriangulationConfig &config,
|
||||||
const PoseBatch3DView *previous_poses_3d,
|
|
||||||
const TriangulationOptions *options_override)
|
const TriangulationOptions *options_override)
|
||||||
{
|
{
|
||||||
return triangulate_debug(poses_2d, config, previous_poses_3d, options_override).final_poses;
|
return triangulate_debug(poses_2d, config, nullptr, options_override).final_poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriangulationResult triangulate_with_report(
|
||||||
|
const PoseBatch2DView &poses_2d,
|
||||||
|
const TriangulationConfig &config,
|
||||||
|
const TrackedPoseBatch3DView &previous_poses_3d,
|
||||||
|
const TriangulationOptions *options_override)
|
||||||
|
{
|
||||||
|
TriangulationTrace trace = triangulate_debug(poses_2d, config, &previous_poses_3d, options_override);
|
||||||
|
return TriangulationResult {
|
||||||
|
std::move(trace.final_poses),
|
||||||
|
std::move(trace.association),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ from collections.abc import Sequence
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from ._core import (
|
from ._core import (
|
||||||
|
AssociationReport,
|
||||||
|
AssociationStatus,
|
||||||
Camera,
|
Camera,
|
||||||
CameraModel,
|
CameraModel,
|
||||||
|
FinalPoseAssociationDebug,
|
||||||
TriangulationConfig,
|
TriangulationConfig,
|
||||||
TriangulationOptions,
|
TriangulationOptions,
|
||||||
|
TriangulationResult,
|
||||||
CoreProposalDebug,
|
CoreProposalDebug,
|
||||||
FullProposalDebug,
|
FullProposalDebug,
|
||||||
GroupingDebug,
|
GroupingDebug,
|
||||||
@@ -22,6 +26,7 @@ from ._core import (
|
|||||||
make_camera,
|
make_camera,
|
||||||
triangulate_debug,
|
triangulate_debug,
|
||||||
triangulate_poses,
|
triangulate_poses,
|
||||||
|
triangulate_with_report,
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -67,8 +72,12 @@ def make_triangulation_config(
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
"Camera",
|
"Camera",
|
||||||
"CameraModel",
|
"CameraModel",
|
||||||
|
"AssociationReport",
|
||||||
|
"AssociationStatus",
|
||||||
|
"FinalPoseAssociationDebug",
|
||||||
"TriangulationConfig",
|
"TriangulationConfig",
|
||||||
"TriangulationOptions",
|
"TriangulationOptions",
|
||||||
|
"TriangulationResult",
|
||||||
"CoreProposalDebug",
|
"CoreProposalDebug",
|
||||||
"FullProposalDebug",
|
"FullProposalDebug",
|
||||||
"GroupingDebug",
|
"GroupingDebug",
|
||||||
@@ -86,4 +95,5 @@ __all__ = [
|
|||||||
"pack_poses_2d",
|
"pack_poses_2d",
|
||||||
"triangulate_debug",
|
"triangulate_debug",
|
||||||
"triangulate_poses",
|
"triangulate_poses",
|
||||||
|
"triangulate_with_report",
|
||||||
]
|
]
|
||||||
|
|||||||
+60
-2
@@ -119,11 +119,19 @@ def test_triangulate_accepts_empty_previous_poses():
|
|||||||
poses_2d, person_counts, cameras = load_case("data/p1/sample.json", "tests/poses_p1.json")
|
poses_2d, person_counts, cameras = load_case("data/p1/sample.json", "tests/poses_p1.json")
|
||||||
config = make_config(cameras, [[5.6, 6.4, 2.4], [0.0, -0.5, 1.2]])
|
config = make_config(cameras, [[5.6, 6.4, 2.4], [0.0, -0.5, 1.2]])
|
||||||
empty_previous = np.zeros((0, len(JOINT_NAMES), 4), dtype=np.float32)
|
empty_previous = np.zeros((0, len(JOINT_NAMES), 4), dtype=np.float32)
|
||||||
|
empty_previous_ids = np.zeros((0,), dtype=np.int64)
|
||||||
|
|
||||||
baseline = rpt.triangulate_poses(poses_2d, person_counts, config)
|
baseline = rpt.triangulate_poses(poses_2d, person_counts, config)
|
||||||
with_previous = rpt.triangulate_poses(poses_2d, person_counts, config, empty_previous)
|
result = rpt.triangulate_with_report(
|
||||||
|
poses_2d,
|
||||||
|
person_counts,
|
||||||
|
config,
|
||||||
|
empty_previous,
|
||||||
|
empty_previous_ids,
|
||||||
|
)
|
||||||
|
|
||||||
np.testing.assert_allclose(with_previous, baseline, rtol=1e-5, atol=1e-5)
|
np.testing.assert_allclose(result.poses_3d, baseline, rtol=1e-5, atol=1e-5)
|
||||||
|
assert result.association.unmatched_previous_track_ids == []
|
||||||
|
|
||||||
|
|
||||||
def test_triangulate_debug_matches_final_output():
|
def test_triangulate_debug_matches_final_output():
|
||||||
@@ -135,6 +143,7 @@ def test_triangulate_debug_matches_final_output():
|
|||||||
|
|
||||||
np.testing.assert_allclose(trace.final_poses, final_poses, rtol=1e-5, atol=1e-5)
|
np.testing.assert_allclose(trace.final_poses, final_poses, rtol=1e-5, atol=1e-5)
|
||||||
assert len(trace.pairs) >= len(trace.core_proposals)
|
assert len(trace.pairs) >= len(trace.core_proposals)
|
||||||
|
assert trace.association.pose_previous_track_ids == []
|
||||||
for group in trace.grouping.groups:
|
for group in trace.grouping.groups:
|
||||||
assert all(0 <= index < len(trace.core_proposals) for index in group.proposal_indices)
|
assert all(0 <= index < len(trace.core_proposals) for index in group.proposal_indices)
|
||||||
for merge_indices in trace.merge.group_proposal_indices:
|
for merge_indices in trace.merge.group_proposal_indices:
|
||||||
@@ -145,18 +154,67 @@ def test_filter_pairs_with_previous_poses_returns_debug_matches():
|
|||||||
poses_2d, person_counts, cameras = load_case("data/p1/sample.json", "tests/poses_p1.json")
|
poses_2d, person_counts, cameras = load_case("data/p1/sample.json", "tests/poses_p1.json")
|
||||||
config = make_config(cameras, [[5.6, 6.4, 2.4], [0.0, -0.5, 1.2]])
|
config = make_config(cameras, [[5.6, 6.4, 2.4], [0.0, -0.5, 1.2]])
|
||||||
previous_poses = rpt.triangulate_poses(poses_2d, person_counts, config)
|
previous_poses = rpt.triangulate_poses(poses_2d, person_counts, config)
|
||||||
|
previous_track_ids = np.arange(previous_poses.shape[0], dtype=np.int64) + 100
|
||||||
|
|
||||||
debug = rpt.filter_pairs_with_previous_poses(
|
debug = rpt.filter_pairs_with_previous_poses(
|
||||||
poses_2d,
|
poses_2d,
|
||||||
person_counts,
|
person_counts,
|
||||||
config,
|
config,
|
||||||
previous_poses,
|
previous_poses,
|
||||||
|
previous_track_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert debug.used_previous_poses is True
|
assert debug.used_previous_poses is True
|
||||||
assert len(debug.matches) == len(rpt.build_pair_candidates(poses_2d, person_counts))
|
assert len(debug.matches) == len(rpt.build_pair_candidates(poses_2d, person_counts))
|
||||||
assert len(debug.kept_pairs) == len(debug.kept_pair_indices)
|
assert len(debug.kept_pairs) == len(debug.kept_pair_indices)
|
||||||
assert any(match.matched_view1 or match.matched_view2 for match in debug.matches)
|
assert any(match.matched_view1 or match.matched_view2 for match in debug.matches)
|
||||||
|
assert any(match.previous_track_id >= 100 for match in debug.matches if match.previous_pose_index >= 0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_triangulate_with_report_resolves_previous_track_ids():
|
||||||
|
poses_2d, person_counts, cameras = load_case("data/p1/sample.json", "tests/poses_p1.json")
|
||||||
|
config = make_config(cameras, [[5.6, 6.4, 2.4], [0.0, -0.5, 1.2]])
|
||||||
|
previous_poses = rpt.triangulate_poses(poses_2d, person_counts, config)
|
||||||
|
previous_track_ids = np.arange(previous_poses.shape[0], dtype=np.int64) + 100
|
||||||
|
|
||||||
|
result = rpt.triangulate_with_report(
|
||||||
|
poses_2d,
|
||||||
|
person_counts,
|
||||||
|
config,
|
||||||
|
previous_poses,
|
||||||
|
previous_track_ids,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.poses_3d.shape == previous_poses.shape
|
||||||
|
assert len(result.association.pose_previous_track_ids) == result.poses_3d.shape[0]
|
||||||
|
matched_track_ids = sorted(
|
||||||
|
track_id for track_id in result.association.pose_previous_track_ids if track_id >= 0
|
||||||
|
)
|
||||||
|
unmatched_track_ids = sorted(result.association.unmatched_previous_track_ids)
|
||||||
|
|
||||||
|
for pose_index in result.association.new_pose_indices:
|
||||||
|
assert result.association.pose_previous_track_ids[pose_index] == -1
|
||||||
|
for pose_index in result.association.ambiguous_pose_indices:
|
||||||
|
assert result.association.pose_previous_track_ids[pose_index] == -1
|
||||||
|
|
||||||
|
assert matched_track_ids == sorted(set(matched_track_ids))
|
||||||
|
assert sorted(matched_track_ids + unmatched_track_ids) == list(previous_track_ids)
|
||||||
|
|
||||||
|
|
||||||
|
def test_triangulate_with_report_rejects_duplicate_previous_track_ids():
|
||||||
|
poses_2d, person_counts, cameras = load_case("data/p1/sample.json", "tests/poses_p1.json")
|
||||||
|
config = make_config(cameras, [[5.6, 6.4, 2.4], [0.0, -0.5, 1.2]])
|
||||||
|
previous_poses = rpt.triangulate_poses(poses_2d, person_counts, config)
|
||||||
|
duplicate_ids = np.zeros((previous_poses.shape[0],), dtype=np.int64)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="unique"):
|
||||||
|
rpt.triangulate_with_report(
|
||||||
|
poses_2d,
|
||||||
|
person_counts,
|
||||||
|
config,
|
||||||
|
previous_poses,
|
||||||
|
duplicate_ids,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_triangulate_does_not_mutate_inputs():
|
def test_triangulate_does_not_mutate_inputs():
|
||||||
|
|||||||
Reference in New Issue
Block a user