evaluation/UAV-benchmark-MOTD_v1.0/utils/clearMOTMex.cpp (388 lines of code) (raw):
#include <matrix.h>
#include "mex.h"
#include <cmath>
#include <omp.h>
#include <set>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
#include <unordered_map>
using namespace std;
inline int index(int i, int j, int numRows) // 2D 0-indexed to C
{
return j*numRows + i;
}
inline double euclidean(double x1, double y1, double x2, double y2)
{
return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}
double boxiou(double l1, double t1, double w1, double h1, double l2, double t2, double w2, double h2)
{
double area1 = w1 * h1;
double area2 = w2 * h2;
double x_overlap = max(0.0, min(l1 + w1, l2 + w2) - max(l1, l2));
double y_overlap = max(0.0, min(t1 + h1, t2 + h2) - max(t1, t2));
double intersectionArea = x_overlap*y_overlap;
double unionArea = area1 + area2 - intersectionArea;
double iou = intersectionArea / unionArea;
return iou;
}
// Min cost bipartite matching via shortest augmenting paths
//
// Code from https://github.com/jaehyunp/
//
// This is an O(n^3) implementation of a shortest augmenting path
// algorithm for finding min cost perfect matchings in dense
// graphs. In practice, it solves 1000x1000 problems in around 1
// second.
//
// cost[i][j] = cost for pairing left node i with right node j
// Lmate[i] = index of right node that left node i pairs with
// Rmate[j] = index of left node that right node j pairs with
//
// The values in cost[i][j] may be positive or negative. To perform
// maximization, simply negate the cost[][] matrix.
typedef vector<double> VD;
typedef vector<VD> VVD;
typedef vector<int> VI;
double MinCostMatching(const VVD &cost, VI &Lmate, VI &Rmate) {
int n = int(cost.size());
// construct dual feasible solution
VD u(n);
VD v(n);
for (int i = 0; i < n; i++) {
u[i] = cost[i][0];
for (int j = 1; j < n; j++) u[i] = min(u[i], cost[i][j]);
}
for (int j = 0; j < n; j++) {
v[j] = cost[0][j] - u[0];
for (int i = 1; i < n; i++) v[j] = min(v[j], cost[i][j] - u[i]);
}
// construct primal solution satisfying complementary slackness
Lmate = VI(n, -1);
Rmate = VI(n, -1);
int mated = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (Rmate[j] != -1) continue;
if (fabs(cost[i][j] - u[i] - v[j]) < 1e-10) {
Lmate[i] = j;
Rmate[j] = i;
mated++;
break;
}
}
}
VD dist(n);
VI dad(n);
VI seen(n);
// repeat until primal solution is feasible
while (mated < n) {
// find an unmatched left node
int s = 0;
while (Lmate[s] != -1) s++;
// initialize Dijkstra
fill(dad.begin(), dad.end(), -1);
fill(seen.begin(), seen.end(), 0);
for (int k = 0; k < n; k++)
dist[k] = cost[s][k] - u[s] - v[k];
int j = 0;
while (true) {
// find closest
j = -1;
for (int k = 0; k < n; k++) {
if (seen[k]) continue;
if (j == -1 || dist[k] < dist[j]) j = k;
}
seen[j] = 1;
// termination condition
if (Rmate[j] == -1) break;
// relax neighbors
const int i = Rmate[j];
for (int k = 0; k < n; k++) {
if (seen[k]) continue;
const double new_dist = dist[j] + cost[i][k] - u[i] - v[k];
if (dist[k] > new_dist) {
dist[k] = new_dist;
dad[k] = j;
}
}
}
// update dual variables
for (int k = 0; k < n; k++) {
if (k == j || !seen[k]) continue;
const int i = Rmate[k];
v[k] += dist[k] - dist[j];
u[i] -= dist[k] - dist[j];
}
u[s] += dist[j];
// augment along path
while (dad[j] >= 0) {
const int d = dad[j];
Rmate[j] = Rmate[d];
Lmate[Rmate[j]] = j;
j = d;
}
Rmate[j] = s;
Lmate[s] = j;
mated++;
}
double value = 0;
for (int i = 0; i < n; i++)
value += cost[i][Lmate[i]];
return value;
}
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
// gtMat and resMat should contain IDs in range [1,...,numIDs]
// and frames in range [1, ..., numFrames]
// data format: frame, ID, left, top, width, height, worldX, worldY
double *gtMat = mxGetPr(prhs[0]);
double *resMat = mxGetPr(prhs[1]);
double threshold = (double)mxGetScalar(prhs[2]);
bool world = (bool)mxGetScalar(prhs[3]);
bool VERBOSE = (bool)mxGetScalar(prhs[4]);
int *dimGT, *dimST;
dimGT = (int *)mxGetDimensions(prhs[0]);
dimST = (int *)mxGetDimensions(prhs[1]);
int rowsGT = dimGT[0], colsGT = dimGT[1], rowsST = dimST[0], colsST = dimST[1];
int Fgt=0, Ngt = 0, Nst = 0;
for (int i = 0; i < rowsGT; i++) {
int frame = gtMat[index(i,0,rowsGT)];
int ID = gtMat[index(i,1,rowsGT)];
if (frame > Fgt) Fgt = frame;
if (ID > Ngt) Ngt = ID;
}
for (int i = 0; i < rowsST; i++) {
int frame = resMat[index(i, 0, rowsST)];
if (frame > Fgt) Fgt = frame;
int ID = resMat[index(i, 1, rowsST)];
if (ID > Nst) Nst = ID;
}
plhs[0] = mxCreateDoubleMatrix(1, Fgt, mxREAL);
plhs[1] = mxCreateDoubleMatrix(1, Fgt, mxREAL);
plhs[2] = mxCreateDoubleMatrix(1, Fgt, mxREAL);
plhs[3] = mxCreateDoubleMatrix(1, Fgt, mxREAL);
plhs[4] = mxCreateDoubleMatrix(1, Fgt, mxREAL);
plhs[5] = mxCreateDoubleMatrix(Fgt, Ngt, mxREAL);
plhs[6] = mxCreateDoubleMatrix(Fgt, Ngt, mxREAL);
plhs[7] = mxCreateDoubleMatrix(Fgt, Nst, mxREAL);
double* mmeOut = mxGetPr(plhs[0]);
double* cOut = mxGetPr(plhs[1]);
double* fpOut = mxGetPr(plhs[2]);
double* mOut = mxGetPr(plhs[3]);
double* gOut = mxGetPr(plhs[4]);
double* dOut = mxGetPr(plhs[5]);
double* MOut = mxGetPr(plhs[6]);
double* allfalseposOut = mxGetPr(plhs[7]);
//double* MOut = mxGetPr(plhs[8]);
double INF = 1e9;
vector<unordered_map<int, int>> gtInd(Fgt);
vector<unordered_map<int, int>> stInd(Fgt);
vector<unordered_map<int,int>> M(Fgt);
vector<int> mme(Fgt, 0); // ID Switchtes(mismatches)
vector<int> c(Fgt, 0); // matches found
vector<int> fp(Fgt, 0); // false positives
vector<int> m(Fgt, 0); // misses = false negatives
vector<int> g(Fgt, 0); // gt count for each frame
vector<vector<double>> d(Fgt, vector<double>(Ngt, 0)); // all distances mapped to [0..1]
vector<vector<int>> allfalsepos(Fgt, vector<int>(Nst, 0));
for (int i = 0; i < rowsGT; i++) {
int frame = gtMat[index(i, 0, rowsGT)]-1;
int ID = gtMat[index(i, 1, rowsGT)]-1;
gtInd[frame][ID] = i;
}
for (int i = 0; i < rowsST; i++) {
int frame = resMat[index(i, 0, rowsST)]-1;
int ID = resMat[index(i, 1, rowsST)]-1;
stInd[frame][ID] = i;
}
for (int i = 0; i < Fgt; i++) {
for (unordered_map<int, int>::iterator it = gtInd[i].begin(); it != gtInd[i].end(); it++) g[i]++;
}
for (int t = 0; t < Fgt; t++)
{
//if ((t + 1) % 1000 == 0) mexEvalString("fprintf('.');"); // print every 1000th frame
if (t > 0)
{
vector<int> mappings;
for (unordered_map<int,int>::iterator it = M[t - 1].begin(); it != M[t - 1].end(); it++) mappings.push_back(it->first);
sort(mappings.begin(), mappings.end());
for (int k = 0; k < mappings.size(); k++)
{
unordered_map<int, int>::const_iterator foundGtind = gtInd[t].find(mappings[k]);
unordered_map<int, int>::const_iterator foundStind = stInd[t].find(M[t - 1][mappings[k]]);
if (foundGtind != gtInd[t].end() && foundStind != stInd[t].end())
{
bool matched = false;
if (world)
{
double gtx, gty, stx, sty;
int rowgt = gtInd[t][mappings[k]];
int rowres = stInd[t][M[t - 1][mappings[k]]];
gtx = gtMat[index(rowgt, 6, rowsGT)];
gty = gtMat[index(rowgt, 7, rowsGT)];
stx = resMat[index(rowres, 6, rowsST)];
sty = resMat[index(rowres, 7, rowsST)];
double dist = euclidean(gtx, gty, stx, sty);
matched = (dist <= threshold);
}
else
{
double gtleft, gttop, gtwidth, gtheight, stleft, sttop, stwidth, stheight;
int rowgt = gtInd[t][mappings[k]];
int rowres = stInd[t][M[t - 1][mappings[k]]];
gtleft = gtMat[index(rowgt, 2, rowsGT)];
gttop = gtMat[index(rowgt, 3, rowsGT)];
gtwidth = gtMat[index(rowgt, 4, rowsGT)];
gtheight = gtMat[index(rowgt, 5, rowsGT)];
stleft = resMat[index(rowres, 2, rowsST)];
sttop = resMat[index(rowres, 3, rowsST)];
stwidth = resMat[index(rowres, 4, rowsST)];
stheight = resMat[index(rowres, 5, rowsST)];
double dist = 1 - boxiou(gtleft, gttop, gtwidth, gtheight, stleft, sttop, stwidth, stheight);
matched = (dist <= threshold);
}
if (matched) {
M[t][mappings[k]] = M[t - 1][mappings[k]];
if (VERBOSE) printf("%d: preserve %d\n", t+1, mappings[k]+1);
}
}
}
}
vector<int> unmappedGt, unmappedEs, stindt, findm;
for (unordered_map<int,int>::iterator it = gtInd[t].begin(); it != gtInd[t].end(); it++) {
unordered_map<int, int>::const_iterator found = M[t].find(it->first);
if (found==M[t].end()) unmappedGt.push_back(it->first);
}
for (unordered_map<int,int>::iterator it = M[t].begin(); it != M[t].end(); it++) findm.push_back(it->second);
for (unordered_map<int,int>::iterator it = stInd[t].begin(); it != stInd[t].end(); it++) stindt.push_back(it->first);
sort(stindt.begin(), stindt.end());
sort(findm.begin(), findm.end());
set_difference(stindt.begin(), stindt.end(), findm.begin(), findm.end(), inserter(unmappedEs, unmappedEs.end()));
sort(unmappedGt.begin(), unmappedGt.end());
int squareSize = max(unmappedGt.size(), unmappedEs.size());
vector<vector<double>> alldist(squareSize, vector<double>(squareSize, INF));
if (VERBOSE)
{
printf("%d: UnmappedGTs: ", t+1);
for (int i = 0; i < unmappedGt.size(); i++) printf("%d, ", unmappedGt[i]+1);
printf("\n%d: UnmappedEs: ", t+1);
for (int i = 0; i < unmappedEs.size(); i++) printf("%d, ", unmappedEs[i]+1);
printf("\n");
}
int uid = 0; // Unique identifier
for (int i = 0; i < unmappedGt.size(); i++)
{
for (int j = 0; j < unmappedEs.size(); j++)
{
int o = unmappedGt[i];
int e = unmappedEs[j];
if (world)
{
double gtx, gty, stx, sty;
int rowgt = gtInd[t][o];
int rowres = stInd[t][e];
gtx = gtMat[index(rowgt, 6, rowsGT)];
gty = gtMat[index(rowgt, 7, rowsGT)];
stx = resMat[index(rowres, 6, rowsST)];
sty = resMat[index(rowres, 7, rowsST)];
double dist = euclidean(gtx, gty, stx, sty);
if (dist <= threshold)
{
alldist[i][j] = dist;
// Add unique identifier to break ties
alldist[i][j] += 1e-9 * uid;
uid++;
}
}
else
{
double gtleft, gttop, gtwidth, gtheight, stleft, sttop, stwidth, stheight;
int rowgt = gtInd[t][o];
int rowres = stInd[t][e];
gtleft = gtMat[index(rowgt, 2, rowsGT)];
gttop = gtMat[index(rowgt, 3, rowsGT)];
gtwidth = gtMat[index(rowgt, 4, rowsGT)];
gtheight = gtMat[index(rowgt, 5, rowsGT)];
stleft = resMat[index(rowres, 2, rowsST)];
sttop = resMat[index(rowres, 3, rowsST)];
stwidth = resMat[index(rowres, 4, rowsST)];
stheight = resMat[index(rowres, 5, rowsST)];
double dist = 1 - boxiou(gtleft, gttop, gtwidth, gtheight, stleft, sttop, stwidth, stheight);
if (dist <= threshold)
{
alldist[i][j] = dist;
// Add unique identifier to break ties
alldist[i][j] += 1e-9 * uid;
uid++;
}
}
}
}
vector<int> Lmate, Rmate;
MinCostMatching(alldist, Lmate, Rmate);
for (int k = 0; k < Lmate.size(); k++) {
if (alldist[k][Lmate[k]] == INF) continue;
M[t][unmappedGt[k]] = unmappedEs[Lmate[k]];
if (VERBOSE) printf("%d: map %d with %d\n", t+1, unmappedGt[k]+1, unmappedEs[Lmate[k]]+1);
}
vector<int> curtracked, alltrackers, mappedtrackers, falsepositives, set1;
for (unordered_map<int,int>::iterator it = M[t].begin(); it != M[t].end(); it++) {
curtracked.push_back(it->first);
set1.push_back(it->second);
}
for (unordered_map<int,int>::iterator it = stInd[t].begin(); it != stInd[t].end(); it++) alltrackers.push_back(it->first);
sort(set1.begin(), set1.end());
sort(alltrackers.begin(), alltrackers.end());
set_intersection(set1.begin(), set1.end(), alltrackers.begin(), alltrackers.end(), inserter(mappedtrackers, mappedtrackers.begin()));
set_difference(alltrackers.begin(), alltrackers.end(), mappedtrackers.begin(), mappedtrackers.end(), inserter(falsepositives, falsepositives.end()));
for (int k = 0; k < falsepositives.size(); k++) allfalsepos[t][falsepositives[k]] = falsepositives[k];
// mismatch errors
if (t > 0)
{
for (int k = 0; k < curtracked.size(); k++)
{
int ct = curtracked[k];
int lastnonempty = -1;
for (int j = t - 1; j >= 0; j--) {
if (M[j].find(ct) != M[j].end()) {
lastnonempty = j; break;
}
}
if (gtInd[t-1].find(ct)!=gtInd[t-1].end() && lastnonempty != -1)
{
int mtct = -1, mlastnonemptyct = -1;
if (M[t].find(ct) != M[t].end()) mtct = M[t][ct];
if (M[lastnonempty].find(ct) != M[lastnonempty].end()) mlastnonemptyct = M[lastnonempty][ct];
if (mtct != mlastnonemptyct)
mme[t]++;
}
}
}
c[t] = curtracked.size();
for (int k = 0; k < curtracked.size(); k++)
{
int ct = curtracked[k];
int eid = M[t][ct];
if (world)
{
double gtx, gty, stx, sty;
int rowgt = gtInd[t][ct];
int rowres = stInd[t][eid];
gtx = gtMat[index(rowgt, 6, rowsGT)];
gty = gtMat[index(rowgt, 7, rowsGT)];
stx = resMat[index(rowres, 6, rowsST)];
sty = resMat[index(rowres, 7, rowsST)];
d[t][ct] = euclidean(gtx, gty, stx, sty);
}
else
{
double gtleft, gttop, gtwidth, gtheight, stleft, sttop, stwidth, stheight;
int rowgt = gtInd[t][ct];
int rowres = stInd[t][eid];
gtleft = gtMat[index(rowgt, 2, rowsGT)];
gttop = gtMat[index(rowgt, 3, rowsGT)];
gtwidth = gtMat[index(rowgt, 4, rowsGT)];
gtheight = gtMat[index(rowgt, 5, rowsGT)];
stleft = resMat[index(rowres, 2, rowsST)];
sttop = resMat[index(rowres, 3, rowsST)];
stwidth = resMat[index(rowres, 4, rowsST)];
stheight = resMat[index(rowres, 5, rowsST)];
d[t][ct] = 1 - boxiou(gtleft, gttop, gtwidth, gtheight, stleft, sttop, stwidth, stheight);
}
}
for (unordered_map<int,int>::iterator it = stInd[t].begin(); it != stInd[t].end(); it++) fp[t]++;
fp[t] -= c[t];
m[t] = g[t] - c[t];
}
// Copy back to matlab
for (int k = 0; k < Fgt; k++) {
mmeOut[k] = mme[k];
cOut[k] = c[k];
fpOut[k] = fp[k];
gOut[k] = g[k];
mOut[k] = m[k];
}
for (int i = 0; i < Fgt; i++) {
for (int j = 0; j < Ngt; j++) {
dOut[index(i, j, Fgt)] = d[i][j];
dOut[index(i, j, Fgt)] = (d[i][j] == INF ? mxGetInf() : d[i][j]);
}
for (unordered_map<int, int>::iterator it = M[i].begin(); it != M[i].end(); it++) {
int j = it->first;
MOut[index(i, j, Fgt)] = M[i][j] + 1; // matlab indexed
}
for (int j = 0; j < Nst; j++) {
allfalseposOut[index(i, j, Fgt)] = allfalsepos[i][j];
}
}
}