You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Merge remote-tracking branch 'origin/develop' into effect-parenting
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
* text=auto
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +1,34 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Hungarian.h: Header file for Class HungarianAlgorithm.
|
||||
//
|
||||
// This is a C++ wrapper with slight modification of a hungarian algorithm implementation by Markus Buehren.
|
||||
// The original implementation is a few mex-functions for use in MATLAB, found here:
|
||||
// http://www.mathworks.com/matlabcentral/fileexchange/6543-functions-for-the-rectangular-assignment-problem
|
||||
//
|
||||
// Both this code and the orignal code are published under the BSD license.
|
||||
// by Cong Ma, 2016
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cfloat>
|
||||
#include <math.h>
|
||||
|
||||
class HungarianAlgorithm
|
||||
{
|
||||
public:
|
||||
HungarianAlgorithm();
|
||||
~HungarianAlgorithm();
|
||||
double Solve(std::vector<std::vector<double>> &DistMatrix, std::vector<int> &Assignment);
|
||||
|
||||
private:
|
||||
void assignmentoptimal(int *assignment, double *cost, double *distMatrix, int nOfRows, int nOfColumns);
|
||||
void buildassignmentvector(int *assignment, bool *starMatrix, int nOfRows, int nOfColumns);
|
||||
void computeassignmentcost(int *assignment, double *cost, double *distMatrix, int nOfRows);
|
||||
void step2a(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim);
|
||||
void step2b(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim);
|
||||
void step3(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim);
|
||||
void step4(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim, int row, int col);
|
||||
void step5(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim);
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Hungarian.h: Header file for Class HungarianAlgorithm.
|
||||
//
|
||||
// This is a C++ wrapper with slight modification of a hungarian algorithm implementation by Markus Buehren.
|
||||
// The original implementation is a few mex-functions for use in MATLAB, found here:
|
||||
// http://www.mathworks.com/matlabcentral/fileexchange/6543-functions-for-the-rectangular-assignment-problem
|
||||
//
|
||||
// Both this code and the orignal code are published under the BSD license.
|
||||
// by Cong Ma, 2016
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cfloat>
|
||||
#include <math.h>
|
||||
|
||||
class HungarianAlgorithm
|
||||
{
|
||||
public:
|
||||
HungarianAlgorithm();
|
||||
~HungarianAlgorithm();
|
||||
double Solve(std::vector<std::vector<double>> &DistMatrix, std::vector<int> &Assignment);
|
||||
|
||||
private:
|
||||
void assignmentoptimal(int *assignment, double *cost, double *distMatrix, int nOfRows, int nOfColumns);
|
||||
void buildassignmentvector(int *assignment, bool *starMatrix, int nOfRows, int nOfColumns);
|
||||
void computeassignmentcost(int *assignment, double *cost, double *distMatrix, int nOfRows);
|
||||
void step2a(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim);
|
||||
void step2b(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim);
|
||||
void step3(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim);
|
||||
void step4(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim, int row, int col);
|
||||
void step5(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim);
|
||||
};
|
||||
|
||||
@@ -1,116 +1,116 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// KalmanTracker.cpp: KalmanTracker Class Implementation Declaration
|
||||
|
||||
#include "KalmanTracker.h"
|
||||
#include <ctime>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// initialize Kalman filter
|
||||
void KalmanTracker::init_kf(
|
||||
StateType stateMat)
|
||||
{
|
||||
int stateNum = 7;
|
||||
int measureNum = 4;
|
||||
kf = KalmanFilter(stateNum, measureNum, 0);
|
||||
|
||||
measurement = Mat::zeros(measureNum, 1, CV_32F);
|
||||
|
||||
kf.transitionMatrix = (Mat_<float>(7, 7) << 1, 0, 0, 0, 1, 0, 0,
|
||||
|
||||
0, 1, 0, 0, 0, 1, 0,
|
||||
0, 0, 1, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 1);
|
||||
|
||||
setIdentity(kf.measurementMatrix);
|
||||
setIdentity(kf.processNoiseCov, Scalar::all(1e-1));
|
||||
setIdentity(kf.measurementNoiseCov, Scalar::all(1e-4));
|
||||
setIdentity(kf.errorCovPost, Scalar::all(1e-2));
|
||||
|
||||
// initialize state vector with bounding box in [cx,cy,s,r] style
|
||||
kf.statePost.at<float>(0, 0) = stateMat.x + stateMat.width / 2;
|
||||
kf.statePost.at<float>(1, 0) = stateMat.y + stateMat.height / 2;
|
||||
kf.statePost.at<float>(2, 0) = stateMat.area();
|
||||
kf.statePost.at<float>(3, 0) = stateMat.width / stateMat.height;
|
||||
}
|
||||
|
||||
// Predict the estimated bounding box.
|
||||
StateType KalmanTracker::predict()
|
||||
{
|
||||
// predict
|
||||
Mat p = kf.predict();
|
||||
m_age += 1;
|
||||
|
||||
if (m_time_since_update > 0)
|
||||
m_hit_streak = 0;
|
||||
m_time_since_update += 1;
|
||||
|
||||
StateType predictBox = get_rect_xysr(p.at<float>(0, 0), p.at<float>(1, 0), p.at<float>(2, 0), p.at<float>(3, 0));
|
||||
|
||||
m_history.push_back(predictBox);
|
||||
return m_history.back();
|
||||
}
|
||||
|
||||
StateType KalmanTracker::predict2()
|
||||
{
|
||||
// predict
|
||||
Mat p = kf.predict();
|
||||
|
||||
StateType predictBox = get_rect_xysr(p.at<float>(0, 0), p.at<float>(1, 0), p.at<float>(2, 0), p.at<float>(3, 0));
|
||||
|
||||
return predictBox;
|
||||
}
|
||||
|
||||
// Update the state vector with observed bounding box.
|
||||
void KalmanTracker::update(
|
||||
StateType stateMat)
|
||||
{
|
||||
m_time_since_update = 0;
|
||||
m_history.clear();
|
||||
m_hits += 1;
|
||||
m_hit_streak += 1;
|
||||
|
||||
// measurement
|
||||
measurement.at<float>(0, 0) = stateMat.x + stateMat.width / 2;
|
||||
measurement.at<float>(1, 0) = stateMat.y + stateMat.height / 2;
|
||||
measurement.at<float>(2, 0) = stateMat.area();
|
||||
measurement.at<float>(3, 0) = stateMat.width / stateMat.height;
|
||||
|
||||
// update
|
||||
kf.correct(measurement);
|
||||
// time_t now = time(0);
|
||||
// convert now to string form
|
||||
|
||||
// detect_times.push_back(dt);
|
||||
}
|
||||
|
||||
// Return the current state vector
|
||||
StateType KalmanTracker::get_state()
|
||||
{
|
||||
Mat s = kf.statePost;
|
||||
return get_rect_xysr(s.at<float>(0, 0), s.at<float>(1, 0), s.at<float>(2, 0), s.at<float>(3, 0));
|
||||
}
|
||||
|
||||
// Convert bounding box from [cx,cy,s,r] to [x,y,w,h] style.
|
||||
StateType KalmanTracker::get_rect_xysr(
|
||||
float cx,
|
||||
float cy,
|
||||
float s,
|
||||
float r)
|
||||
{
|
||||
float w = sqrt(s * r);
|
||||
float h = s / w;
|
||||
float x = (cx - w / 2);
|
||||
float y = (cy - h / 2);
|
||||
|
||||
if (x < 0 && cx > 0)
|
||||
x = 0;
|
||||
if (y < 0 && cy > 0)
|
||||
y = 0;
|
||||
|
||||
return StateType(x, y, w, h);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// KalmanTracker.cpp: KalmanTracker Class Implementation Declaration
|
||||
|
||||
#include "KalmanTracker.h"
|
||||
#include <ctime>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// initialize Kalman filter
|
||||
void KalmanTracker::init_kf(
|
||||
StateType stateMat)
|
||||
{
|
||||
int stateNum = 7;
|
||||
int measureNum = 4;
|
||||
kf = KalmanFilter(stateNum, measureNum, 0);
|
||||
|
||||
measurement = Mat::zeros(measureNum, 1, CV_32F);
|
||||
|
||||
kf.transitionMatrix = (Mat_<float>(7, 7) << 1, 0, 0, 0, 1, 0, 0,
|
||||
|
||||
0, 1, 0, 0, 0, 1, 0,
|
||||
0, 0, 1, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 1);
|
||||
|
||||
setIdentity(kf.measurementMatrix);
|
||||
setIdentity(kf.processNoiseCov, Scalar::all(1e-1));
|
||||
setIdentity(kf.measurementNoiseCov, Scalar::all(1e-4));
|
||||
setIdentity(kf.errorCovPost, Scalar::all(1e-2));
|
||||
|
||||
// initialize state vector with bounding box in [cx,cy,s,r] style
|
||||
kf.statePost.at<float>(0, 0) = stateMat.x + stateMat.width / 2;
|
||||
kf.statePost.at<float>(1, 0) = stateMat.y + stateMat.height / 2;
|
||||
kf.statePost.at<float>(2, 0) = stateMat.area();
|
||||
kf.statePost.at<float>(3, 0) = stateMat.width / stateMat.height;
|
||||
}
|
||||
|
||||
// Predict the estimated bounding box.
|
||||
StateType KalmanTracker::predict()
|
||||
{
|
||||
// predict
|
||||
Mat p = kf.predict();
|
||||
m_age += 1;
|
||||
|
||||
if (m_time_since_update > 0)
|
||||
m_hit_streak = 0;
|
||||
m_time_since_update += 1;
|
||||
|
||||
StateType predictBox = get_rect_xysr(p.at<float>(0, 0), p.at<float>(1, 0), p.at<float>(2, 0), p.at<float>(3, 0));
|
||||
|
||||
m_history.push_back(predictBox);
|
||||
return m_history.back();
|
||||
}
|
||||
|
||||
StateType KalmanTracker::predict2()
|
||||
{
|
||||
// predict
|
||||
Mat p = kf.predict();
|
||||
|
||||
StateType predictBox = get_rect_xysr(p.at<float>(0, 0), p.at<float>(1, 0), p.at<float>(2, 0), p.at<float>(3, 0));
|
||||
|
||||
return predictBox;
|
||||
}
|
||||
|
||||
// Update the state vector with observed bounding box.
|
||||
void KalmanTracker::update(
|
||||
StateType stateMat)
|
||||
{
|
||||
m_time_since_update = 0;
|
||||
m_history.clear();
|
||||
m_hits += 1;
|
||||
m_hit_streak += 1;
|
||||
|
||||
// measurement
|
||||
measurement.at<float>(0, 0) = stateMat.x + stateMat.width / 2;
|
||||
measurement.at<float>(1, 0) = stateMat.y + stateMat.height / 2;
|
||||
measurement.at<float>(2, 0) = stateMat.area();
|
||||
measurement.at<float>(3, 0) = stateMat.width / stateMat.height;
|
||||
|
||||
// update
|
||||
kf.correct(measurement);
|
||||
// time_t now = time(0);
|
||||
// convert now to string form
|
||||
|
||||
// detect_times.push_back(dt);
|
||||
}
|
||||
|
||||
// Return the current state vector
|
||||
StateType KalmanTracker::get_state()
|
||||
{
|
||||
Mat s = kf.statePost;
|
||||
return get_rect_xysr(s.at<float>(0, 0), s.at<float>(1, 0), s.at<float>(2, 0), s.at<float>(3, 0));
|
||||
}
|
||||
|
||||
// Convert bounding box from [cx,cy,s,r] to [x,y,w,h] style.
|
||||
StateType KalmanTracker::get_rect_xysr(
|
||||
float cx,
|
||||
float cy,
|
||||
float s,
|
||||
float r)
|
||||
{
|
||||
float w = sqrt(s * r);
|
||||
float h = s / w;
|
||||
float x = (cx - w / 2);
|
||||
float y = (cy - h / 2);
|
||||
|
||||
if (x < 0 && cx > 0)
|
||||
x = 0;
|
||||
if (y < 0 && cy > 0)
|
||||
y = 0;
|
||||
|
||||
return StateType(x, y, w, h);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// KalmanTracker.h: KalmanTracker Class Declaration
|
||||
|
||||
#ifndef KALMAN_H
|
||||
#define KALMAN_H 2
|
||||
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/highgui/highgui.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// KalmanTracker.h: KalmanTracker Class Declaration
|
||||
|
||||
#ifndef KALMAN_H
|
||||
#define KALMAN_H 2
|
||||
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/highgui/highgui.hpp"
|
||||
|
||||
|
||||
#define StateType cv::Rect_<float>
|
||||
|
||||
@@ -14,52 +14,52 @@
|
||||
class KalmanTracker
|
||||
{
|
||||
public:
|
||||
KalmanTracker()
|
||||
{
|
||||
init_kf(StateType());
|
||||
m_time_since_update = 0;
|
||||
m_hits = 0;
|
||||
m_hit_streak = 0;
|
||||
m_age = 0;
|
||||
m_id = 0;
|
||||
}
|
||||
KalmanTracker(StateType initRect, float confidence, int classId, int objectId) : confidence(confidence), classId(classId)
|
||||
{
|
||||
init_kf(initRect);
|
||||
m_time_since_update = 0;
|
||||
m_hits = 0;
|
||||
m_hit_streak = 0;
|
||||
m_age = 0;
|
||||
m_id = objectId;
|
||||
}
|
||||
|
||||
~KalmanTracker()
|
||||
{
|
||||
m_history.clear();
|
||||
}
|
||||
|
||||
StateType predict();
|
||||
StateType predict2();
|
||||
void update(StateType stateMat);
|
||||
|
||||
StateType get_state();
|
||||
StateType get_rect_xysr(float cx, float cy, float s, float r);
|
||||
|
||||
int m_time_since_update;
|
||||
int m_hits;
|
||||
int m_hit_streak;
|
||||
int m_age;
|
||||
int m_id;
|
||||
float confidence;
|
||||
int classId;
|
||||
|
||||
private:
|
||||
void init_kf(StateType stateMat);
|
||||
|
||||
cv::KalmanFilter kf;
|
||||
cv::Mat measurement;
|
||||
|
||||
std::vector<StateType> m_history;
|
||||
};
|
||||
|
||||
KalmanTracker()
|
||||
{
|
||||
init_kf(StateType());
|
||||
m_time_since_update = 0;
|
||||
m_hits = 0;
|
||||
m_hit_streak = 0;
|
||||
m_age = 0;
|
||||
m_id = 0;
|
||||
}
|
||||
KalmanTracker(StateType initRect, float confidence, int classId, int objectId) : confidence(confidence), classId(classId)
|
||||
{
|
||||
init_kf(initRect);
|
||||
m_time_since_update = 0;
|
||||
m_hits = 0;
|
||||
m_hit_streak = 0;
|
||||
m_age = 0;
|
||||
m_id = objectId;
|
||||
}
|
||||
|
||||
~KalmanTracker()
|
||||
{
|
||||
m_history.clear();
|
||||
}
|
||||
|
||||
StateType predict();
|
||||
StateType predict2();
|
||||
void update(StateType stateMat);
|
||||
|
||||
StateType get_state();
|
||||
StateType get_rect_xysr(float cx, float cy, float s, float r);
|
||||
|
||||
int m_time_since_update;
|
||||
int m_hits;
|
||||
int m_hit_streak;
|
||||
int m_age;
|
||||
int m_id;
|
||||
float confidence;
|
||||
int classId;
|
||||
|
||||
private:
|
||||
void init_kf(StateType stateMat);
|
||||
|
||||
cv::KalmanFilter kf;
|
||||
cv::Mat measurement;
|
||||
|
||||
std::vector<StateType> m_history;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,211 +1,211 @@
|
||||
#include "sort.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Constructor
|
||||
SortTracker::SortTracker(int max_age, int min_hits)
|
||||
{
|
||||
_min_hits = min_hits;
|
||||
_max_age = max_age;
|
||||
alive_tracker = true;
|
||||
}
|
||||
|
||||
// Computes IOU between two bounding boxes
|
||||
double SortTracker::GetIOU(cv::Rect_<float> bb_test, cv::Rect_<float> bb_gt)
|
||||
{
|
||||
float in = (bb_test & bb_gt).area();
|
||||
float un = bb_test.area() + bb_gt.area() - in;
|
||||
|
||||
if (un < DBL_EPSILON)
|
||||
return 0;
|
||||
|
||||
return (double)(in / un);
|
||||
}
|
||||
|
||||
// Computes centroid distance between two bounding boxes
|
||||
double SortTracker::GetCentroidsDistance(
|
||||
cv::Rect_<float> bb_test,
|
||||
cv::Rect_<float> bb_gt)
|
||||
{
|
||||
float bb_test_centroid_x = (bb_test.x + bb_test.width / 2);
|
||||
float bb_test_centroid_y = (bb_test.y + bb_test.height / 2);
|
||||
|
||||
float bb_gt_centroid_x = (bb_gt.x + bb_gt.width / 2);
|
||||
float bb_gt_centroid_y = (bb_gt.y + bb_gt.height / 2);
|
||||
|
||||
double distance = (double)sqrt(pow(bb_gt_centroid_x - bb_test_centroid_x, 2) + pow(bb_gt_centroid_y - bb_test_centroid_y, 2));
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
void SortTracker::update(vector<cv::Rect> detections_cv, int frame_count, double image_diagonal, std::vector<float> confidences, std::vector<int> classIds)
|
||||
{
|
||||
vector<TrackingBox> detections;
|
||||
if (trackers.size() == 0) // the first frame met
|
||||
{
|
||||
alive_tracker = false;
|
||||
// initialize kalman trackers using first detections.
|
||||
for (unsigned int i = 0; i < detections_cv.size(); i++)
|
||||
{
|
||||
TrackingBox tb;
|
||||
|
||||
tb.box = cv::Rect_<float>(detections_cv[i]);
|
||||
tb.classId = classIds[i];
|
||||
tb.confidence = confidences[i];
|
||||
detections.push_back(tb);
|
||||
|
||||
KalmanTracker trk = KalmanTracker(detections[i].box, detections[i].confidence, detections[i].classId, i);
|
||||
trackers.push_back(trk);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = 0; i < detections_cv.size(); i++)
|
||||
{
|
||||
TrackingBox tb;
|
||||
tb.box = cv::Rect_<float>(detections_cv[i]);
|
||||
tb.classId = classIds[i];
|
||||
tb.confidence = confidences[i];
|
||||
detections.push_back(tb);
|
||||
}
|
||||
for (auto it = frameTrackingResult.begin(); it != frameTrackingResult.end(); it++)
|
||||
{
|
||||
int frame_age = frame_count - it->frame;
|
||||
if (frame_age >= _max_age || frame_age < 0)
|
||||
{
|
||||
dead_trackers_id.push_back(it->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// 3.1. get predicted locations from existing trackers.
|
||||
predictedBoxes.clear();
|
||||
for (unsigned int i = 0; i < trackers.size();)
|
||||
{
|
||||
cv::Rect_<float> pBox = trackers[i].predict();
|
||||
if (pBox.x >= 0 && pBox.y >= 0)
|
||||
{
|
||||
predictedBoxes.push_back(pBox);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
trackers.erase(trackers.begin() + i);
|
||||
}
|
||||
|
||||
trkNum = predictedBoxes.size();
|
||||
detNum = detections.size();
|
||||
|
||||
centroid_dist_matrix.clear();
|
||||
centroid_dist_matrix.resize(trkNum, vector<double>(detNum, 0));
|
||||
|
||||
for (unsigned int i = 0; i < trkNum; i++) // compute iou matrix as a distance matrix
|
||||
{
|
||||
for (unsigned int j = 0; j < detNum; j++)
|
||||
{
|
||||
// use 1-iou because the hungarian algorithm computes a minimum-cost assignment.
|
||||
double distance = SortTracker::GetCentroidsDistance(predictedBoxes[i], detections[j].box) / image_diagonal;
|
||||
centroid_dist_matrix[i][j] = distance;
|
||||
}
|
||||
}
|
||||
|
||||
HungarianAlgorithm HungAlgo;
|
||||
assignment.clear();
|
||||
HungAlgo.Solve(centroid_dist_matrix, assignment);
|
||||
// find matches, unmatched_detections and unmatched_predictions
|
||||
unmatchedTrajectories.clear();
|
||||
unmatchedDetections.clear();
|
||||
allItems.clear();
|
||||
matchedItems.clear();
|
||||
|
||||
if (detNum > trkNum) // there are unmatched detections
|
||||
{
|
||||
for (unsigned int n = 0; n < detNum; n++)
|
||||
allItems.insert(n);
|
||||
|
||||
for (unsigned int i = 0; i < trkNum; ++i)
|
||||
matchedItems.insert(assignment[i]);
|
||||
|
||||
set_difference(allItems.begin(), allItems.end(),
|
||||
matchedItems.begin(), matchedItems.end(),
|
||||
insert_iterator<set<int>>(unmatchedDetections, unmatchedDetections.begin()));
|
||||
}
|
||||
else if (detNum < trkNum) // there are unmatched trajectory/predictions
|
||||
{
|
||||
for (unsigned int i = 0; i < trkNum; ++i)
|
||||
if (assignment[i] == -1) // unassigned label will be set as -1 in the assignment algorithm
|
||||
unmatchedTrajectories.insert(i);
|
||||
}
|
||||
else
|
||||
;
|
||||
|
||||
// filter out matched with low IOU
|
||||
matchedPairs.clear();
|
||||
for (unsigned int i = 0; i < trkNum; ++i)
|
||||
{
|
||||
if (assignment[i] == -1) // pass over invalid values
|
||||
continue;
|
||||
if (centroid_dist_matrix[i][assignment[i]] > max_centroid_dist_norm)
|
||||
{
|
||||
unmatchedTrajectories.insert(i);
|
||||
unmatchedDetections.insert(assignment[i]);
|
||||
}
|
||||
else
|
||||
matchedPairs.push_back(cv::Point(i, assignment[i]));
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < matchedPairs.size(); i++)
|
||||
{
|
||||
int trkIdx = matchedPairs[i].x;
|
||||
int detIdx = matchedPairs[i].y;
|
||||
trackers[trkIdx].update(detections[detIdx].box);
|
||||
trackers[trkIdx].classId = detections[detIdx].classId;
|
||||
trackers[trkIdx].confidence = detections[detIdx].confidence;
|
||||
}
|
||||
|
||||
// create and initialise new trackers for unmatched detections
|
||||
for (auto umd : unmatchedDetections)
|
||||
{
|
||||
KalmanTracker tracker = KalmanTracker(detections[umd].box, detections[umd].confidence, detections[umd].classId, umd);
|
||||
trackers.push_back(tracker);
|
||||
}
|
||||
|
||||
for (auto it2 = dead_trackers_id.begin(); it2 != dead_trackers_id.end(); it2++)
|
||||
{
|
||||
for (unsigned int i = 0; i < trackers.size();)
|
||||
{
|
||||
if (trackers[i].m_id == (*it2))
|
||||
{
|
||||
trackers.erase(trackers.begin() + i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// get trackers' output
|
||||
frameTrackingResult.clear();
|
||||
for (unsigned int i = 0; i < trackers.size();)
|
||||
{
|
||||
if ((trackers[i].m_time_since_update < 1 && trackers[i].m_hit_streak >= _min_hits) || frame_count <= _min_hits)
|
||||
{
|
||||
alive_tracker = true;
|
||||
TrackingBox res;
|
||||
res.box = trackers[i].get_state();
|
||||
res.id = trackers[i].m_id;
|
||||
res.frame = frame_count;
|
||||
res.classId = trackers[i].classId;
|
||||
res.confidence = trackers[i].confidence;
|
||||
frameTrackingResult.push_back(res);
|
||||
}
|
||||
|
||||
// remove dead tracklet
|
||||
if (trackers[i].m_time_since_update >= _max_age)
|
||||
{
|
||||
trackers.erase(trackers.begin() + i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#include "sort.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Constructor
|
||||
SortTracker::SortTracker(int max_age, int min_hits)
|
||||
{
|
||||
_min_hits = min_hits;
|
||||
_max_age = max_age;
|
||||
alive_tracker = true;
|
||||
}
|
||||
|
||||
// Computes IOU between two bounding boxes
|
||||
double SortTracker::GetIOU(cv::Rect_<float> bb_test, cv::Rect_<float> bb_gt)
|
||||
{
|
||||
float in = (bb_test & bb_gt).area();
|
||||
float un = bb_test.area() + bb_gt.area() - in;
|
||||
|
||||
if (un < DBL_EPSILON)
|
||||
return 0;
|
||||
|
||||
return (double)(in / un);
|
||||
}
|
||||
|
||||
// Computes centroid distance between two bounding boxes
|
||||
double SortTracker::GetCentroidsDistance(
|
||||
cv::Rect_<float> bb_test,
|
||||
cv::Rect_<float> bb_gt)
|
||||
{
|
||||
float bb_test_centroid_x = (bb_test.x + bb_test.width / 2);
|
||||
float bb_test_centroid_y = (bb_test.y + bb_test.height / 2);
|
||||
|
||||
float bb_gt_centroid_x = (bb_gt.x + bb_gt.width / 2);
|
||||
float bb_gt_centroid_y = (bb_gt.y + bb_gt.height / 2);
|
||||
|
||||
double distance = (double)sqrt(pow(bb_gt_centroid_x - bb_test_centroid_x, 2) + pow(bb_gt_centroid_y - bb_test_centroid_y, 2));
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
void SortTracker::update(vector<cv::Rect> detections_cv, int frame_count, double image_diagonal, std::vector<float> confidences, std::vector<int> classIds)
|
||||
{
|
||||
vector<TrackingBox> detections;
|
||||
if (trackers.size() == 0) // the first frame met
|
||||
{
|
||||
alive_tracker = false;
|
||||
// initialize kalman trackers using first detections.
|
||||
for (unsigned int i = 0; i < detections_cv.size(); i++)
|
||||
{
|
||||
TrackingBox tb;
|
||||
|
||||
tb.box = cv::Rect_<float>(detections_cv[i]);
|
||||
tb.classId = classIds[i];
|
||||
tb.confidence = confidences[i];
|
||||
detections.push_back(tb);
|
||||
|
||||
KalmanTracker trk = KalmanTracker(detections[i].box, detections[i].confidence, detections[i].classId, i);
|
||||
trackers.push_back(trk);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = 0; i < detections_cv.size(); i++)
|
||||
{
|
||||
TrackingBox tb;
|
||||
tb.box = cv::Rect_<float>(detections_cv[i]);
|
||||
tb.classId = classIds[i];
|
||||
tb.confidence = confidences[i];
|
||||
detections.push_back(tb);
|
||||
}
|
||||
for (auto it = frameTrackingResult.begin(); it != frameTrackingResult.end(); it++)
|
||||
{
|
||||
int frame_age = frame_count - it->frame;
|
||||
if (frame_age >= _max_age || frame_age < 0)
|
||||
{
|
||||
dead_trackers_id.push_back(it->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// 3.1. get predicted locations from existing trackers.
|
||||
predictedBoxes.clear();
|
||||
for (unsigned int i = 0; i < trackers.size();)
|
||||
{
|
||||
cv::Rect_<float> pBox = trackers[i].predict();
|
||||
if (pBox.x >= 0 && pBox.y >= 0)
|
||||
{
|
||||
predictedBoxes.push_back(pBox);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
trackers.erase(trackers.begin() + i);
|
||||
}
|
||||
|
||||
trkNum = predictedBoxes.size();
|
||||
detNum = detections.size();
|
||||
|
||||
centroid_dist_matrix.clear();
|
||||
centroid_dist_matrix.resize(trkNum, vector<double>(detNum, 0));
|
||||
|
||||
for (unsigned int i = 0; i < trkNum; i++) // compute iou matrix as a distance matrix
|
||||
{
|
||||
for (unsigned int j = 0; j < detNum; j++)
|
||||
{
|
||||
// use 1-iou because the hungarian algorithm computes a minimum-cost assignment.
|
||||
double distance = SortTracker::GetCentroidsDistance(predictedBoxes[i], detections[j].box) / image_diagonal;
|
||||
centroid_dist_matrix[i][j] = distance;
|
||||
}
|
||||
}
|
||||
|
||||
HungarianAlgorithm HungAlgo;
|
||||
assignment.clear();
|
||||
HungAlgo.Solve(centroid_dist_matrix, assignment);
|
||||
// find matches, unmatched_detections and unmatched_predictions
|
||||
unmatchedTrajectories.clear();
|
||||
unmatchedDetections.clear();
|
||||
allItems.clear();
|
||||
matchedItems.clear();
|
||||
|
||||
if (detNum > trkNum) // there are unmatched detections
|
||||
{
|
||||
for (unsigned int n = 0; n < detNum; n++)
|
||||
allItems.insert(n);
|
||||
|
||||
for (unsigned int i = 0; i < trkNum; ++i)
|
||||
matchedItems.insert(assignment[i]);
|
||||
|
||||
set_difference(allItems.begin(), allItems.end(),
|
||||
matchedItems.begin(), matchedItems.end(),
|
||||
insert_iterator<set<int>>(unmatchedDetections, unmatchedDetections.begin()));
|
||||
}
|
||||
else if (detNum < trkNum) // there are unmatched trajectory/predictions
|
||||
{
|
||||
for (unsigned int i = 0; i < trkNum; ++i)
|
||||
if (assignment[i] == -1) // unassigned label will be set as -1 in the assignment algorithm
|
||||
unmatchedTrajectories.insert(i);
|
||||
}
|
||||
else
|
||||
;
|
||||
|
||||
// filter out matched with low IOU
|
||||
matchedPairs.clear();
|
||||
for (unsigned int i = 0; i < trkNum; ++i)
|
||||
{
|
||||
if (assignment[i] == -1) // pass over invalid values
|
||||
continue;
|
||||
if (centroid_dist_matrix[i][assignment[i]] > max_centroid_dist_norm)
|
||||
{
|
||||
unmatchedTrajectories.insert(i);
|
||||
unmatchedDetections.insert(assignment[i]);
|
||||
}
|
||||
else
|
||||
matchedPairs.push_back(cv::Point(i, assignment[i]));
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < matchedPairs.size(); i++)
|
||||
{
|
||||
int trkIdx = matchedPairs[i].x;
|
||||
int detIdx = matchedPairs[i].y;
|
||||
trackers[trkIdx].update(detections[detIdx].box);
|
||||
trackers[trkIdx].classId = detections[detIdx].classId;
|
||||
trackers[trkIdx].confidence = detections[detIdx].confidence;
|
||||
}
|
||||
|
||||
// create and initialise new trackers for unmatched detections
|
||||
for (auto umd : unmatchedDetections)
|
||||
{
|
||||
KalmanTracker tracker = KalmanTracker(detections[umd].box, detections[umd].confidence, detections[umd].classId, umd);
|
||||
trackers.push_back(tracker);
|
||||
}
|
||||
|
||||
for (auto it2 = dead_trackers_id.begin(); it2 != dead_trackers_id.end(); it2++)
|
||||
{
|
||||
for (unsigned int i = 0; i < trackers.size();)
|
||||
{
|
||||
if (trackers[i].m_id == (*it2))
|
||||
{
|
||||
trackers.erase(trackers.begin() + i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// get trackers' output
|
||||
frameTrackingResult.clear();
|
||||
for (unsigned int i = 0; i < trackers.size();)
|
||||
{
|
||||
if ((trackers[i].m_time_since_update < 1 && trackers[i].m_hit_streak >= _min_hits) || frame_count <= _min_hits)
|
||||
{
|
||||
alive_tracker = true;
|
||||
TrackingBox res;
|
||||
res.box = trackers[i].get_state();
|
||||
res.id = trackers[i].m_id;
|
||||
res.frame = frame_count;
|
||||
res.classId = trackers[i].classId;
|
||||
res.confidence = trackers[i].confidence;
|
||||
frameTrackingResult.push_back(res);
|
||||
}
|
||||
|
||||
// remove dead tracklet
|
||||
if (trackers[i].m_time_since_update >= _max_age)
|
||||
{
|
||||
trackers.erase(trackers.begin() + i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
|
||||
#include "KalmanTracker.h"
|
||||
#include "Hungarian.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip> // to format image names using setw() and setfill()
|
||||
#include <set>
|
||||
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/highgui/highgui.hpp"
|
||||
|
||||
#ifndef _OPENCV_KCFTRACKER_HPP_
|
||||
#define _OPENCV_KCFTRACKER_HPP_
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
typedef struct TrackingBox
|
||||
{
|
||||
int frame = 0;
|
||||
float confidence = 0;
|
||||
int classId = 0;
|
||||
int id = 0;
|
||||
cv::Rect_<float> box = cv::Rect_<float>(0.0, 0.0, 0.0, 0.0);
|
||||
TrackingBox() {}
|
||||
TrackingBox(int _frame, float _confidence, int _classId, int _id) : frame(_frame), confidence(_confidence), classId(_classId), id(_id) {}
|
||||
} TrackingBox;
|
||||
|
||||
class SortTracker
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
SortTracker(int max_age = 7, int min_hits = 2);
|
||||
// Initialize tracker
|
||||
|
||||
// Update position based on the new frame
|
||||
void update(std::vector<cv::Rect> detection, int frame_count, double image_diagonal, std::vector<float> confidences, std::vector<int> classIds);
|
||||
double GetIOU(cv::Rect_<float> bb_test, cv::Rect_<float> bb_gt);
|
||||
double GetCentroidsDistance(cv::Rect_<float> bb_test, cv::Rect_<float> bb_gt);
|
||||
std::vector<KalmanTracker> trackers;
|
||||
|
||||
double max_centroid_dist_norm = 0.05;
|
||||
|
||||
std::vector<cv::Rect_<float>> predictedBoxes;
|
||||
std::vector<std::vector<double>> centroid_dist_matrix;
|
||||
std::vector<int> assignment;
|
||||
std::set<int> unmatchedDetections;
|
||||
std::set<int> unmatchedTrajectories;
|
||||
std::set<int> allItems;
|
||||
std::set<int> matchedItems;
|
||||
std::vector<cv::Point> matchedPairs;
|
||||
|
||||
std::vector<TrackingBox> frameTrackingResult;
|
||||
std::vector<int> dead_trackers_id;
|
||||
|
||||
unsigned int trkNum = 0;
|
||||
unsigned int detNum = 0;
|
||||
int _min_hits;
|
||||
int _max_age;
|
||||
bool alive_tracker;
|
||||
};
|
||||
|
||||
#include "KalmanTracker.h"
|
||||
#include "Hungarian.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip> // to format image names using setw() and setfill()
|
||||
#include <set>
|
||||
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/highgui/highgui.hpp"
|
||||
|
||||
#ifndef _OPENCV_KCFTRACKER_HPP_
|
||||
#define _OPENCV_KCFTRACKER_HPP_
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
typedef struct TrackingBox
|
||||
{
|
||||
int frame = 0;
|
||||
float confidence = 0;
|
||||
int classId = 0;
|
||||
int id = 0;
|
||||
cv::Rect_<float> box = cv::Rect_<float>(0.0, 0.0, 0.0, 0.0);
|
||||
TrackingBox() {}
|
||||
TrackingBox(int _frame, float _confidence, int _classId, int _id) : frame(_frame), confidence(_confidence), classId(_classId), id(_id) {}
|
||||
} TrackingBox;
|
||||
|
||||
class SortTracker
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
SortTracker(int max_age = 7, int min_hits = 2);
|
||||
// Initialize tracker
|
||||
|
||||
// Update position based on the new frame
|
||||
void update(std::vector<cv::Rect> detection, int frame_count, double image_diagonal, std::vector<float> confidences, std::vector<int> classIds);
|
||||
double GetIOU(cv::Rect_<float> bb_test, cv::Rect_<float> bb_gt);
|
||||
double GetCentroidsDistance(cv::Rect_<float> bb_test, cv::Rect_<float> bb_gt);
|
||||
std::vector<KalmanTracker> trackers;
|
||||
|
||||
double max_centroid_dist_norm = 0.05;
|
||||
|
||||
std::vector<cv::Rect_<float>> predictedBoxes;
|
||||
std::vector<std::vector<double>> centroid_dist_matrix;
|
||||
std::vector<int> assignment;
|
||||
std::set<int> unmatchedDetections;
|
||||
std::set<int> unmatchedTrajectories;
|
||||
std::set<int> allItems;
|
||||
std::set<int> matchedItems;
|
||||
std::vector<cv::Point> matchedPairs;
|
||||
|
||||
std::vector<TrackingBox> frameTrackingResult;
|
||||
std::vector<int> dead_trackers_id;
|
||||
|
||||
unsigned int trkNum = 0;
|
||||
unsigned int detNum = 0;
|
||||
int _min_hits;
|
||||
int _max_age;
|
||||
bool alive_tracker;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user