Skip to content

Commit 71c0a30

Browse files
authored
Merge pull request #490 from Nuzhny007/master
New linear assignment algorithm - Jonker-Volgenant / LAPJV algorithm
2 parents 80ddc25 + 5942871 commit 71c0a30

File tree

8 files changed

+452
-43
lines changed

8 files changed

+452
-43
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[![CodeQL](https://github.com/Smorodov/Multitarget-tracker/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Smorodov/Multitarget-tracker/actions/workflows/codeql-analysis.yml)
66

77
## Latest Features
8+
- New linear assignment algorithm - [Jonker-Volgenant / LAPJV algorithm](https://github.com/yongyanghz/LAPJV-algorithm-c) used in [scipy](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linear_sum_assignment.html) as alternative for Hungarian allgorithm
89
- D-FINE detector works with TensorRT! Export pre-trained PyTorch models [here (Peterande/D-FINE)](https://github.com/Peterande/D-FINE) to ONNX format and run Multitarget-tracker with `-e=6` example
910
- RF-DETR detector works with TensorRT! Export pre-trained PyTorch models [here (roboflow/rf-detr)](https://github.com/roboflow/rf-detr) to ONNX format and run Multitarget-tracker with `-e=6` example
1011
- YOLOv12 detector works with TensorRT! Export pre-trained PyTorch models [here (sunsmarterjie/yolov12)](https://github.com/sunsmarterjie/yolov12) to ONNX format and run Multitarget-tracker with `-e=6` example

src/Tracker/CMakeLists.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ set(tracker_sources
1616
TrackerSettings.cpp
1717

1818
HungarianAlg/HungarianAlg.cpp
19+
LAPJV_algorithm/lap.cpp
1920

2021
VOTTracker.hpp
21-
dat/dat_tracker.cpp
22-
)
22+
dat/dat_tracker.cpp)
23+
2324
set(tracker_headers
2425
Ctracker.h
2526
ShortPathCalculator.h
@@ -28,9 +29,10 @@ set(tracker_headers
2829
Kalman.h
2930
TrackerSettings.h
3031
HungarianAlg/HungarianAlg.h
32+
LAPJV_algorithm/lap.h
3133
EmbeddingsCalculator.hpp
32-
dat/dat_tracker.hpp
33-
)
34+
dat/dat_tracker.hpp)
35+
3436
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm|ARM|aarch64|AARCH64")
3537

3638
else()

src/Tracker/Ctracker.cpp

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <fstream>
12
#include "Ctracker.h"
23
#include "ShortPathCalculator.h"
34
#include "EmbeddingsCalculator.hpp"
@@ -64,6 +65,9 @@ CTracker::CTracker(const TrackerSettings& settings)
6465
case tracking::MatchBipart:
6566
m_SPCalculator = std::make_unique<SPBipart>(spSettings);
6667
break;
68+
case tracking::MatchLAPJV:
69+
m_SPCalculator = std::make_unique<SPLAPJV>(spSettings);
70+
break;
6771
}
6872
assert(m_SPCalculator);
6973

@@ -167,16 +171,16 @@ void CTracker::UpdateTrackingState(const regions_t& regions,
167171
cv::UMat currFrame,
168172
float fps)
169173
{
170-
const size_t N = m_tracks.size(); // Tracking objects
171-
const size_t M = regions.size(); // Detections or regions
174+
const size_t colsTracks = m_tracks.size(); // Tracking objects
175+
const size_t rowsRegions = regions.size(); // Detections or regions
172176

173-
assignments_t assignment(N, -1); // Assignments regions -> tracks
177+
assignments_t assignmentT2R(colsTracks, -1); // Assignments: index - track, value - region
174178

175179
std::vector<RegionEmbedding> regionEmbeddings;
176180
CalcEmbeddins(regionEmbeddings, regions, currFrame);
177181

178182
#if DRAW_DBG_ASSIGNMENT
179-
std::cout << "CTracker::UpdateTrackingState: m_tracks = " << N << ", regions = " << M << std::endl;
183+
std::cout << "CTracker::UpdateTrackingState: m_tracks = " << colsTracks << ", regions = " << rowsRegions << std::endl;
180184

181185
int fontType = cv::FONT_HERSHEY_TRIPLEX;
182186
double fontSize = 0.6;
@@ -241,7 +245,7 @@ void CTracker::UpdateTrackingState(const regions_t& regions,
241245
#if DRAW_DBG_ASSIGNMENT
242246
std::cout << "CTracker::UpdateTrackingState: Distance matrix between all tracks to all regions" << std::endl;
243247
#endif
244-
distMatrix_t costMatrix(N * M);
248+
distMatrix_t costMatrix(colsTracks * rowsRegions);
245249
const track_t maxPossibleCost = std::max(static_cast<track_t>(1.), static_cast<track_t>(currFrame.cols * currFrame.rows));
246250
track_t maxCost = 0;
247251
CreateDistaceMatrix(regions, regionEmbeddings, costMatrix, maxPossibleCost, maxCost);
@@ -259,39 +263,63 @@ void CTracker::UpdateTrackingState(const regions_t& regions,
259263
#if DRAW_DBG_ASSIGNMENT
260264
std::cout << "CTracker::UpdateTrackingState: Solving assignment problem (shortest paths)" << std::endl;
261265
#endif
262-
m_SPCalculator->Solve(costMatrix, N, M, assignment, maxCost);
266+
m_SPCalculator->Solve(costMatrix, colsTracks, rowsRegions, assignmentT2R, maxCost);
267+
268+
#if 0
269+
{
270+
static size_t saveSolveNum = 0;
271+
std::ofstream resCSV("mt_example" + std::to_string(saveSolveNum) + ".csv");
272+
for (size_t r = 0; r < rowsRegions; ++r)
273+
{
274+
for (size_t c = 0; c < colsTracks; ++c)
275+
{
276+
if (c == colsTracks - 1)
277+
resCSV << std::fixed << std::setw(2) << std::setprecision(2) << costMatrix[c + r * colsTracks] << std::endl;
278+
else
279+
resCSV << std::fixed << std::setw(2) << std::setprecision(2) << costMatrix[c + r * colsTracks] << ", ";
280+
}
281+
}
282+
std::ofstream resSol("mt_example" + std::to_string(saveSolveNum) + ".sol");
283+
resSol << maxCost << std::endl;
284+
for (size_t r = 0; r < assignmentT2R.size(); ++r)
285+
{
286+
resSol << assignmentT2R[r] << std::endl;
287+
}
288+
++saveSolveNum;
289+
}
290+
#endif
263291

264292
// Clean assignment from pairs with large distance
265293
#if DRAW_DBG_ASSIGNMENT
266294
std::cout << "CTracker::UpdateTrackingState: Clean assignment from pairs with large distance" << std::endl;
267295
#endif
268-
for (size_t i = 0; i < assignment.size(); ++i)
296+
for (size_t i = 0; i < assignmentT2R.size(); ++i)
269297
{
270298
#if DRAW_DBG_ASSIGNMENT
271299
std::stringstream ss;
272-
if (assignment[i] != -1)
300+
if (assignmentT2R[i] != -1)
273301
{
274-
ss << m_tracks[i]->GetID().ID2Str() << "-" << assignment[i] << ": " << std::fixed << std::setprecision(2) << costMatrix[i + assignment[i] * N];
302+
ss << m_tracks[i]->GetID().ID2Str() << "-" << assignmentT2R[i] << ": " << std::fixed << std::setprecision(2) << costMatrix[i + assignmentT2R[i] * colsTracks];
275303

276-
if (costMatrix[i + assignment[i] * N] > m_settings.m_distThres)
304+
if (costMatrix[i + assignmentT2R[i] * colsTracks] > m_settings.m_distThres)
277305
{
278306
ss << ">" << m_settings.m_distThres;
279-
cv::line(dbgAssignment, m_tracks[i]->GetLastRect().center, regions[assignment[i]].m_rrect.center, colorMatchedAboveThreshRed, 2);
307+
cv::line(dbgAssignment, m_tracks[i]->GetLastRect().center, regions[assignmentT2R[i]].m_rrect.center, colorMatchedAboveThreshRed, 2);
280308
DrawRRect(dbgAssignment, m_tracks[i]->LastRegion().m_rrect, colorMatchedAboveThreshRed, 1);
281309
}
282310
else
283311
{
284312
ss << "<" << m_settings.m_distThres;
285-
cv::line(dbgAssignment, m_tracks[i]->GetLastRect().center, regions[assignment[i]].m_rrect.center, colorMatchedGreen, 1);
313+
cv::line(dbgAssignment, m_tracks[i]->GetLastRect().center, regions[assignmentT2R[i]].m_rrect.center, colorMatchedGreen, 1);
286314
DrawRRect(dbgAssignment, m_tracks[i]->LastRegion().m_rrect, colorMatchedGreen, 1);
287315
}
288316

289317
for (size_t ri = 0; ri < regions.size(); ++ri)
290318
{
291-
if (static_cast<int>(ri) != assignment[i] && costMatrix[i + ri * N] < 1)
319+
if (static_cast<int>(ri) != assignmentT2R[i] && costMatrix[i + ri * colsTracks] < 1)
292320
{
293321
std::stringstream liness;
294-
liness << std::fixed << std::setprecision(2) << costMatrix[i + ri * N];
322+
liness << std::fixed << std::setprecision(2) << costMatrix[i + ri * colsTracks];
295323
auto p1 = m_tracks[i]->GetLastRect().center;
296324
auto p2 = regions[ri].m_rrect.center;
297325
cv::line(dbgAssignment, p1, p2, colorMatchedNearMargenta, 1);
@@ -305,10 +333,10 @@ void CTracker::UpdateTrackingState(const regions_t& regions,
305333
DrawRRect(dbgAssignment, m_tracks[i]->LastRegion().m_rrect, colorNotMatchedNearWhite, 1);
306334
for (size_t ri = 0; ri < regions.size(); ++ri)
307335
{
308-
if (costMatrix[i + ri * N] < 1)
336+
if (costMatrix[i + ri * colsTracks] < 1)
309337
{
310338
std::stringstream liness;
311-
liness << std::fixed << std::setprecision(2) << costMatrix[i + ri * N];
339+
liness << std::fixed << std::setprecision(2) << costMatrix[i + ri * colsTracks];
312340
auto p1 = m_tracks[i]->GetLastRect().center;
313341
auto p2 = regions[ri].m_rrect.center;
314342
cv::line(dbgAssignment, p1, p2, colorNotMatchedNearWhite, 1);
@@ -323,11 +351,11 @@ void CTracker::UpdateTrackingState(const regions_t& regions,
323351
}
324352
#endif
325353

326-
if (assignment[i] != -1)
354+
if (assignmentT2R[i] != -1)
327355
{
328-
if (costMatrix[i + assignment[i] * N] > m_settings.m_distThres)
356+
if (costMatrix[i + assignmentT2R[i] * colsTracks] > m_settings.m_distThres)
329357
{
330-
assignment[i] = -1;
358+
assignmentT2R[i] = -1;
331359
m_tracks[i]->SkippedFrames()++;
332360
}
333361
}
@@ -351,7 +379,7 @@ void CTracker::UpdateTrackingState(const regions_t& regions,
351379
m_removedObjects.push_back(m_tracks[i]->GetID());
352380
//std::cout << "Remove: " << m_tracks[i]->GetID().ID2Str() << ": skipped = " << m_tracks[i]->SkippedFrames() << ", out of frame " << m_tracks[i]->IsOutOfTheFrame() << std::endl;
353381
m_tracks.erase(m_tracks.begin() + i);
354-
assignment.erase(assignment.begin() + i);
382+
assignmentT2R.erase(assignmentT2R.begin() + i);
355383
}
356384
else
357385
{
@@ -368,7 +396,7 @@ void CTracker::UpdateTrackingState(const regions_t& regions,
368396
{
369397
//std::cout << "CTracker::update: regions[" << i << "].m_rrect: " << regions[i].m_rrect.center << ", " << regions[i].m_rrect.angle << ", " << regions[i].m_rrect.size << std::endl;
370398

371-
if (find(assignment.begin(), assignment.end(), i) == assignment.end())
399+
if (std::find(assignmentT2R.begin(), assignmentT2R.end(), i) == assignmentT2R.end())
372400
{
373401
if (regionEmbeddings.empty())
374402
m_tracks.push_back(std::make_unique<CTrack>(regions[i],
@@ -398,22 +426,22 @@ void CTracker::UpdateTrackingState(const regions_t& regions,
398426
std::cout << "CTracker::UpdateTrackingState: Update Kalman Filters state" << std::endl;
399427
#endif
400428

401-
const ptrdiff_t stop_i = static_cast<ptrdiff_t>(assignment.size());
429+
const ptrdiff_t stop_i = static_cast<ptrdiff_t>(assignmentT2R.size());
402430
#pragma omp parallel for
403431
for (ptrdiff_t i = 0; i < stop_i; ++i)
404432
{
405433
// If track updated less than one time, than filter state is not correct.
406-
if (assignment[i] != -1) // If we have assigned detect, then update using its coordinates,
434+
if (assignmentT2R[i] != -1) // If we have assigned detect, then update using its coordinates,
407435
{
408436
m_tracks[i]->SkippedFrames() = 0;
409437
// std::cout << "Update track " << i << " for " << assignment[i] << " region, regionEmbeddings.size = " << regionEmbeddings.size() << std::endl;
410438
if (regionEmbeddings.empty())
411-
m_tracks[i]->Update(regions[assignment[i]],
439+
m_tracks[i]->Update(regions[assignmentT2R[i]],
412440
true, m_settings.m_maxTraceLength,
413441
m_prevFrame, currFrame,
414442
m_settings.m_useAbandonedDetection ? cvRound(m_settings.m_minStaticTime * fps) : 0, m_settings.m_maxSpeedForStatic);
415443
else
416-
m_tracks[i]->Update(regions[assignment[i]], regionEmbeddings[assignment[i]],
444+
m_tracks[i]->Update(regions[assignmentT2R[i]], regionEmbeddings[assignmentT2R[i]],
417445
true, m_settings.m_maxTraceLength,
418446
m_prevFrame, currFrame,
419447
m_settings.m_useAbandonedDetection ? cvRound(m_settings.m_minStaticTime * fps) : 0, m_settings.m_maxSpeedForStatic);
@@ -448,10 +476,10 @@ void CTracker::CreateDistaceMatrix(const regions_t& regions,
448476
track_t maxPossibleCost,
449477
track_t& maxCost)
450478
{
451-
const size_t N = m_tracks.size(); // Tracking objects
479+
const size_t colsTracks = m_tracks.size(); // Tracking objects
452480
maxCost = 0;
453481

454-
for (size_t i = 0; i < N; ++i)
482+
for (size_t i = 0; i < colsTracks; ++i)
455483
{
456484
const auto& track = m_tracks[i];
457485

@@ -574,9 +602,9 @@ void CTracker::CreateDistaceMatrix(const regions_t& regions,
574602
assert(ind == tracking::DistsCount);
575603
}
576604

577-
costMatrix[i + j * N] = dist;
605+
costMatrix[i + j * colsTracks] = dist;
578606
if constexpr (DIST_LOGS)
579-
std::cout << "costMatrix[" << j << "][" << i << "] (or " << (i + j * N) << ") = " << dist << std::endl;
607+
std::cout << "costMatrix[" << j << "][" << i << "] (or " << (i + j * colsTracks) << ") = " << dist << std::endl;
580608

581609
if (dist < 0 || dist > maxPossibleCost)
582610
{

0 commit comments

Comments
 (0)