TTK
Loading...
Searching...
No Matches
TrackingPostProcessing.cpp
Go to the documentation of this file.
2
6
8 const std::vector<std::vector<int>> &trajTime,
9 const std::vector<std::vector<int>> &trajVertexId,
10 const std::vector<std::vector<double>> &coordsX,
11 const std::vector<std::vector<double>> &coordsY,
12 const std::vector<int> &trajCriticalType,
13 std::vector<LinearTrajectory> &linearTraj,
14 std::vector<LinearTrajectory> &outputTraj,
15 std::vector<FuseRecord> &fuseRecords) {
16
17 ttk::Timer timer;
18 const int numTraj = static_cast<int>(trajTime.size());
19 const bool useTypeConstraint
20 = (static_cast<int>(trajCriticalType.size()) == numTraj);
21 this->printMsg("Linear regression + chaining (input: "
22 + std::to_string(numTraj) + " t." + ")");
23
24 auto temporalOk = [&](int sFrame, int eFrame) -> bool {
25 return (sFrame - eFrame > minFrameDist_)
26 && (sFrame - eFrame < maxFrameDist_);
27 };
28
29 auto dist2AtStartFrame
30 = [&](const LinearTrajectory &coefI, const LinearTrajectory &coefJ,
31 int sFrame) -> double {
32 const double xTh = coefI.evalX(sFrame);
33 const double yTh = coefI.evalY(sFrame);
34 const double xJ = coefJ.evalX(sFrame);
35 const double yJ = coefJ.evalY(sFrame);
36 const double dx = xJ - xTh, dy = yJ - yTh;
37 return dx * dx + dy * dy;
38 };
39
40 auto buildSamplesForChain
41 = [&](const std::vector<FuseRecord> &chain, std::vector<int> &T,
42 std::vector<double> &X, std::vector<double> &Y) {
43 const int capacity = static_cast<int>(chain.size()) * 2 + 2;
44 T.reserve(capacity);
45 X.reserve(capacity);
46 Y.reserve(capacity);
47 for(const auto &r : chain) {
48 const std::vector<int> T2{trajTime[r.i].front(), r.endFrame};
49 const auto &cI = linearTraj[r.i];
50 for(const int t : T2) {
51 X.push_back(cI.evalX(t));
52 Y.push_back(cI.evalY(t));
53 T.push_back(t);
54 }
55 }
56 const FuseRecord &r = chain.back();
57 const auto &cJ = linearTraj[r.j];
58 X.push_back(cJ.evalX(r.startFrame));
59 X.push_back(cJ.evalX(trajTime[r.j].back()));
60 Y.push_back(cJ.evalY(r.startFrame));
61 Y.push_back(cJ.evalY(trajTime[r.j].back()));
62 T.push_back(r.startFrame);
63 T.push_back(trajTime[r.j].back());
64 };
65
66 auto fitLineCoefForChain
67 = [&](const std::vector<FuseRecord> &chain) -> LinearTrajectory {
68 std::vector<int> T;
69 std::vector<double> X, Y;
70 buildSamplesForChain(chain, T, X, Y);
71 LinearTrajectory lineCoef;
72#ifdef TTK_ENABLE_EIGEN
73 linearRegression(T, X, Y, lineCoef);
74#else
75 lineCoef = linearTraj[chain.front().i];
76#endif
77 lineCoef.startFrame = trajTime[chain[0].i].front();
78 lineCoef.endFrame = trajTime[chain.back().j].back();
79 return lineCoef;
80 };
81
82 linearTraj.assign(numTraj, LinearTrajectory{});
83 for(int i = 0; i < numTraj; ++i) {
84 if(trajTime[i].empty())
85 continue;
86 linearTraj[i].startFrame = trajTime[i].front();
87 linearTraj[i].endFrame = trajTime[i].back();
88 if(doLinearize_) {
89#ifdef TTK_ENABLE_EIGEN
90 linearRegression(trajTime[i], coordsX[i], coordsY[i], linearTraj[i]);
91#else
92 this->printWrn("Eigen unavailable: skipping linear regression");
93#endif
94 } else {
95 const int tA = trajTime[i].front();
96 const int tB = trajTime[i].back();
97 const double xA = coordsX[i].front();
98 const double xB = coordsX[i].back();
99 const double yA = coordsY[i].front();
100 const double yB = coordsY[i].back();
101 if(tB != tA) {
102 linearTraj[i].ax = (xB - xA) / static_cast<double>(tB - tA);
103 linearTraj[i].ay = (yB - yA) / static_cast<double>(tB - tA);
104 linearTraj[i].bx = xA - linearTraj[i].ax * tA;
105 linearTraj[i].by = yA - linearTraj[i].ay * tA;
106 } else {
107 linearTraj[i].ax = 0.0;
108 linearTraj[i].bx = xA;
109 linearTraj[i].ay = 0.0;
110 linearTraj[i].by = yA;
111 }
112 }
113 }
114
115 outputTraj.clear();
116 fuseRecords.clear();
117
118 if(!doFusion_) {
119 outputTraj.reserve(numTraj);
120 for(int i = 0; i < numTraj; ++i) {
121 if(trajTime[i].empty())
122 continue;
123 LinearTrajectory lt = linearTraj[i];
124 lt.originalTrajId = i;
125 lt.criticalPoints.reserve(trajTime[i].size());
126 for(size_t k = 0; k < trajTime[i].size(); ++k) {
127 lt.criticalPoints.emplace_back(
128 trajTime[i][k], static_cast<ttk::SimplexId>(trajVertexId[i][k]));
129 }
130 linearTraj[i].finalChainId = static_cast<int>(outputTraj.size());
131 lt.finalChainId = linearTraj[i].finalChainId;
132 outputTraj.push_back(std::move(lt));
133 }
134 this->printMsg("Output: " + std::to_string(outputTraj.size()) + " t.", 1.0,
135 timer.getElapsedTime(), this->threadNumber_);
136 return 1;
137 }
138
139 std::vector<std::array<double, 3>> meanDir(numTraj);
140 computeMeanUnitDirectionLinear(linearTraj, meanDir);
141
142 fuseRecords.reserve(numTraj);
143 std::vector<char> usedAsStart(numTraj, false), usedAsEnd(numTraj, false);
144
145 const double similarityThreshold = cosCol_;
146 const double maxLinkDist2 = maxRadius_;
147
148 for(int i = 0; i < numTraj; ++i) {
149 if(usedAsStart[i] || trajTime[i].empty())
150 continue;
151
152 const int endFrame = trajTime[i].back();
153
154 int bestJ = -1;
155 double bestScore = std::numeric_limits<double>::infinity();
156
157 const double dist2Denom = (maxLinkDist2 > 0.0) ? maxLinkDist2 : 1.0;
158 const double dotDenom
159 = (1.0 - similarityThreshold > 1e-12) ? (1.0 - similarityThreshold) : 1.0;
160 const double timeDenom
161 = (maxFrameDist_ > 0) ? static_cast<double>(maxFrameDist_) : 1.0;
162
163 for(int j = 0; j < numTraj; ++j) {
164 if(usedAsEnd[j] || j == i || trajTime[j].empty())
165 continue;
166
167 if(useTypeConstraint && trajCriticalType[i] != trajCriticalType[j])
168 continue;
169
170 const int startFrame = trajTime[j].front();
171
172 const double dist2
173 = dist2AtStartFrame(linearTraj[i], linearTraj[j], startFrame);
174 if(dist2 > maxLinkDist2)
175 continue;
176
177 const double dot = ttk::Geometry::dotProduct<double>(
178 meanDir[i].data(), meanDir[j].data(), 3);
179 if(dot < similarityThreshold)
180 continue;
181
182 if(!temporalOk(startFrame, endFrame))
183 continue;
184
185 const double penDist2 = dist2 / dist2Denom;
186 const double penDot = (1.0 - dot) / dotDenom;
187 const double penTime
188 = static_cast<double>(std::abs(endFrame - startFrame)) / timeDenom;
189
190 const double score = penDist2 + penDot + penTime;
191
192 if(score < bestScore) {
193 bestScore = score;
194 bestJ = j;
195 }
196 }
197 if(bestJ >= 0) {
198 fuseRecords.push_back(
199 {i, bestJ, trajTime[i].back(), trajTime[bestJ].front(), -1});
200 usedAsStart[i] = true;
201 usedAsEnd[bestJ] = true;
202 }
203 }
204
205 outputTraj.reserve(numTraj);
206 std::vector<bool> used(fuseRecords.size(), false);
207
208 for(size_t idx1 = 0; idx1 < fuseRecords.size(); ++idx1) {
209 if(used[idx1])
210 continue;
211
212 auto &r1 = fuseRecords[idx1];
213 const int finalId = static_cast<int>(outputTraj.size());
214
215 std::vector<FuseRecord> chain{r1};
216 r1.finalContrib = finalId;
217 linearTraj[r1.i].finalChainId = finalId;
218 linearTraj[r1.j].finalChainId = finalId;
219 used[idx1] = true;
220
221 // Prepend
222 bool prepended = true;
223 while(prepended) {
224 prepended = false;
225 for(size_t idx2 = 0; idx2 < fuseRecords.size(); ++idx2) {
226 if(used[idx2])
227 continue;
228 auto &r2 = fuseRecords[idx2];
229 if(r2.j == chain.front().i) {
230 chain.insert(chain.begin(), r2);
231 r2.finalContrib = finalId;
232 linearTraj[r2.i].finalChainId = finalId;
233 linearTraj[r2.j].finalChainId = finalId;
234 used[idx2] = true;
235 prepended = true;
236 break;
237 }
238 }
239 }
240
241 // Extend
242 bool extended = true;
243 while(extended) {
244 extended = false;
245 for(size_t idx2 = 0; idx2 < fuseRecords.size(); ++idx2) {
246 if(used[idx2])
247 continue;
248 auto &r2 = fuseRecords[idx2];
249 if(chain.back().j == r2.i) {
250 chain.push_back(r2);
251 r2.finalContrib = finalId;
252 linearTraj[r2.i].finalChainId = finalId;
253 linearTraj[r2.j].finalChainId = finalId;
254 used[idx2] = true;
255 extended = true;
256 break;
257 }
258 }
259 }
260
261 if(doLinearizeFuse_) {
262 LinearTrajectory lineCoef = fitLineCoefForChain(chain);
263 lineCoef.finalChainId = finalId;
264 lineCoef.originalTrajId = -1;
265
266 const int firstTraj = chain[0].i;
267 for(size_t k = 0; k < trajTime[firstTraj].size(); ++k) {
268 lineCoef.criticalPoints.emplace_back(
269 trajTime[firstTraj][k],
270 static_cast<ttk::SimplexId>(trajVertexId[firstTraj][k]));
271 }
272 for(const auto &rec : chain) {
273 const int tj = rec.j;
274 for(size_t k = 0; k < trajTime[tj].size(); ++k) {
275 lineCoef.criticalPoints.emplace_back(
276 trajTime[tj][k], static_cast<ttk::SimplexId>(trajVertexId[tj][k]));
277 }
278 }
279
280 outputTraj.push_back(std::move(lineCoef));
281 } else {
282 const int firstTraj = chain[0].i;
283 {
284 LinearTrajectory seg = linearTraj[firstTraj];
285 seg.startFrame = trajTime[firstTraj].front();
286 seg.endFrame = trajTime[firstTraj].back();
287 seg.finalChainId = finalId;
288 seg.originalTrajId = firstTraj;
289 seg.criticalPoints.reserve(trajTime[firstTraj].size());
290 for(size_t k = 0; k < trajTime[firstTraj].size(); ++k) {
291 seg.criticalPoints.emplace_back(
292 trajTime[firstTraj][k],
293 static_cast<ttk::SimplexId>(trajVertexId[firstTraj][k]));
294 }
295 outputTraj.push_back(std::move(seg));
296 }
297
298 for(const auto &rec : chain) {
299 const int iSeg = rec.i;
300 const int jSeg = rec.j;
301 const int tEnd = trajTime[iSeg].back();
302 const int tStart = trajTime[jSeg].front();
303 const auto &cI = linearTraj[iSeg];
304 const auto &cJ = linearTraj[jSeg];
305
306 LinearTrajectory junction;
307 const double xA = cI.evalX(tEnd);
308 const double yA = cI.evalY(tEnd);
309 const double xB = cJ.evalX(tStart);
310 const double yB = cJ.evalY(tStart);
311 if(tStart != tEnd) {
312 const double dt = static_cast<double>(tStart - tEnd);
313 junction.ax = (xB - xA) / dt;
314 junction.ay = (yB - yA) / dt;
315 junction.bx = xA - junction.ax * static_cast<double>(tEnd);
316 junction.by = yA - junction.ay * static_cast<double>(tEnd);
317 } else {
318 junction.ax = 0.0;
319 junction.ay = 0.0;
320 junction.bx = xA;
321 junction.by = yA;
322 }
323 junction.startFrame = tEnd;
324 junction.endFrame = tStart;
325 junction.finalChainId = finalId;
326 junction.originalTrajId = -1;
327 outputTraj.push_back(std::move(junction));
328
329 LinearTrajectory seg = linearTraj[jSeg];
330 seg.startFrame = trajTime[jSeg].front();
331 seg.endFrame = trajTime[jSeg].back();
332 seg.finalChainId = finalId;
333 seg.originalTrajId = jSeg;
334 seg.criticalPoints.reserve(trajTime[jSeg].size());
335 for(size_t k = 0; k < trajTime[jSeg].size(); ++k) {
336 seg.criticalPoints.emplace_back(
337 trajTime[jSeg][k],
338 static_cast<ttk::SimplexId>(trajVertexId[jSeg][k]));
339 }
340 outputTraj.push_back(std::move(seg));
341 }
342 }
343 }
344
345 for(int i = 0; i < numTraj; ++i) {
346 if(usedAsStart[i] || usedAsEnd[i] || trajTime[i].empty())
347 continue;
348 LinearTrajectory lineCoef = linearTraj[i];
349 lineCoef.startFrame = trajTime[i].front();
350 lineCoef.endFrame = trajTime[i].back();
351 lineCoef.originalTrajId = i;
352 lineCoef.criticalPoints.reserve(trajTime[i].size());
353 for(size_t k = 0; k < trajTime[i].size(); ++k) {
354 lineCoef.criticalPoints.emplace_back(
355 trajTime[i][k], static_cast<ttk::SimplexId>(trajVertexId[i][k]));
356 }
357 linearTraj[i].finalChainId = static_cast<int>(outputTraj.size());
358 lineCoef.finalChainId = linearTraj[i].finalChainId;
359 outputTraj.push_back(std::move(lineCoef));
360 }
361
362 for(auto &c : outputTraj) {
363 if(c.endFrame < c.startFrame)
364 std::swap(c.startFrame, c.endFrame);
365 }
366
367 this->printMsg("Output: " + std::to_string(outputTraj.size()) + " t.", 1.0,
368 timer.getElapsedTime(), this->threadNumber_);
369 return 1;
370}
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:159
void setDebugMsgPrefix(const std::string &prefix)
Definition Debug.h:364
double getElapsedTime()
Definition Timer.h:15
int computeMeanUnitDirectionLinear(const std::vector< LinearTrajectory > &newTraj, std::vector< std::array< double, 3 > > &meanDir)
int correctTrajectory(const std::vector< std::vector< int > > &trajTime, const std::vector< std::vector< int > > &trajVertexId, const std::vector< std::vector< double > > &coordsX, const std::vector< std::vector< double > > &coordsY, const std::vector< int > &trajCriticalType, std::vector< LinearTrajectory > &linearTraj, std::vector< LinearTrajectory > &outputTraj, std::vector< FuseRecord > &fuseRecords)
Linearize + (optional) chain input per-trajectory point clouds.
T dotProduct(const T *vA0, const T *vA1, const T *vB0, const T *vB1)
Definition Geometry.cpp:388
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
Fusion-link record: trajectory i ends and trajectory j starts.
Linear trajectory: x(t) = ax*t + bx, y(t) = ay*t + by, defined on the inclusive frame range [startFra...
std::vector< std::pair< int, ttk::SimplexId > > criticalPoints
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)