TTK
Loading...
Searching...
No Matches
ttkTrackingFromFields.cpp
Go to the documentation of this file.
1#include <vtkDoubleArray.h>
2#include <vtkInformation.h>
3#include <vtkIntArray.h>
4#include <vtkLine.h>
5#include <vtkPointData.h>
6
7#include <cstdio>
8// #include <vtkNew.h>
9// #include <vtkSmartPointer.h>
10
11#include <ttkMacros.h>
14#include <ttkUtils.h>
15
17
19 this->SetNumberOfInputPorts(1);
20 this->SetNumberOfOutputPorts(2);
21}
22
24 vtkInformation *info) {
25 if(port == 0) {
26 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkUnstructuredGrid");
27 return 1;
28 }
29 if(port == 1) {
30 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkDataSet");
31 return 1;
32 }
33 return 0;
34}
36 vtkInformation *info) {
37 if(port == 0) {
38 info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
39 return 1;
40 }
41 return 0;
42}
43
45 vtkInformation *ttkNotUsed(request),
46 vtkInformationVector **inputVector,
47 vtkInformationVector *outputVector) {
48
49 vtkInformation *outInfo = outputVector->GetInformationObject(0);
50 if(outInfo
51 && !vtkUnstructuredGrid::SafeDownCast(
52 outInfo->Get(vtkDataObject::DATA_OBJECT()))) {
53 vtkNew<vtkUnstructuredGrid> ug;
54 outInfo->Set(vtkDataObject::DATA_OBJECT(), ug);
55 }
56
57 vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
58 vtkInformation *outInfo1 = outputVector->GetInformationObject(1);
59 if(!inInfo || !outInfo1)
60 return 0;
61
62 vtkDataObject *inputDO = inInfo->Get(vtkDataObject::DATA_OBJECT());
63 vtkDataObject *currentDO = outInfo1->Get(vtkDataObject::DATA_OBJECT());
64
65 if(inputDO == nullptr)
66 return 0;
67
68 if(currentDO == nullptr || !currentDO->IsA(inputDO->GetClassName())) {
70 = vtkSmartPointer<vtkDataObject>::Take(inputDO->NewInstance());
71 outInfo1->Set(vtkDataObject::DATA_OBJECT(), newDO);
72 }
73
74 return 1;
75}
76
77// (*) Persistence-driven approach
78template <class dataType, class triangulationType>
79int ttkTrackingFromFields::trackWithPersistenceMatching(
80 vtkUnstructuredGrid *output,
81 unsigned long fieldNumber,
82 const triangulationType *triangulation) {
83
84 std::vector<ttk::DiagramType> persistenceDiagrams(fieldNumber);
85
87 (int)fieldNumber, persistenceDiagrams, triangulation);
88
89 std::vector<std::vector<ttk::MatchingType>> outputMatchings(fieldNumber - 1);
90
91 double const spacing = Spacing;
92 std::string const algorithm = DistanceAlgorithm;
93 double const tolerance = Tolerance;
94 std::string const wasserstein = WassersteinMetric;
95
98 tfp.setDebugLevel(this->debugLevel_);
100 (int)fieldNumber, persistenceDiagrams, outputMatchings,
101 algorithm, // Not from paraview, from enclosing tracking plugin
102 wasserstein, tolerance, PX, PY, PZ, PS, PE // Coefficients
103 );
104
105 vtkNew<vtkPoints> const points{};
106 vtkNew<vtkUnstructuredGrid> const persistenceDiagram{};
107
108 vtkNew<vtkDoubleArray> persistenceScalars{};
109 vtkNew<vtkDoubleArray> valueScalars{};
110 vtkNew<vtkIntArray> matchingIdScalars{};
111 vtkNew<vtkIntArray> lengthScalars{};
112 vtkNew<vtkIntArray> timeScalars{};
113 vtkNew<vtkIntArray> componentIds{};
114 vtkNew<vtkIntArray> pointTypeScalars{};
115
116 persistenceScalars->SetName("Cost");
117 valueScalars->SetName("Scalar");
118 matchingIdScalars->SetName("MatchingIdentifier");
119 lengthScalars->SetName("ComponentLength");
120 timeScalars->SetName("TimeStep");
121 componentIds->SetName("ConnectedComponentId");
122 pointTypeScalars->SetName("CriticalType");
123
124 std::vector<ttk::trackingTuple> trackingsBase;
125 tfp.performTracking(persistenceDiagrams, outputMatchings, trackingsBase);
126
127 std::vector<std::set<int>> trackingTupleToMerged(trackingsBase.size());
128
129 if(DoPostProc) {
130 tfp.performPostProcess(persistenceDiagrams, trackingsBase,
131 trackingTupleToMerged, PostProcThresh);
132 }
133
134 bool const useGeometricSpacing = UseGeometricSpacing;
135
136 // Build mesh.
138 trackingsBase, outputMatchings, persistenceDiagrams, useGeometricSpacing,
139 spacing, DoPostProc, trackingTupleToMerged, points, persistenceDiagram,
140 persistenceScalars, valueScalars, matchingIdScalars, lengthScalars,
141 timeScalars, componentIds, pointTypeScalars, *this);
142
143 output->ShallowCopy(persistenceDiagram);
144
145 return 1;
146}
147
148template <class dataType, class triangulationType>
149int ttkTrackingFromFields::trackWithCriticalPointMatching(
150 vtkUnstructuredGrid *output,
151 unsigned long fieldNumber,
152 const triangulationType *triangulation) {
153
154 ttk::Timer t{};
155
156 float x = 0, y = 0, z = 0;
157 float maxX = 0, minX = 0, maxY = 0, minY = 0, maxZ = 0, minZ = 0;
158 triangulation->getVertexPoint(0, minX, minY, minZ);
159 triangulation->getVertexPoint(0, maxX, maxY, maxZ);
160
161 for(int i = 0; i < triangulation->getNumberOfVertices(); i++) {
162 triangulation->getVertexPoint(i, x, y, z);
163 maxX = std::max(x, maxX);
164 maxX = std::min(x, minX);
165 maxY = std::max(y, maxX);
166 minY = std::min(y, minY);
167 maxZ = std::max(z, maxZ);
168 minZ = std::min(z, minZ);
169 }
170
171 double const relativeDestructionCost = RelativeDestructionCost;
172 double const tolerance = (double)Tolerance;
173 float meshDiameter
174 = std::sqrt(std::pow(maxX - minX, 2) + std::pow(maxY - minY, 2)
175 + std::pow(maxZ - minZ, 2));
176 int assignmentMethod = AssignmentMethod;
177
178 ttk::TrackingFromCriticalPoints tracker;
179 tracker.setMeshDiameter(meshDiameter);
180 tracker.setTolerance(tolerance);
181 tracker.setEpsilon(relativeDestructionCost);
182 tracker.setAssignmentMethod(assignmentMethod);
183 tracker.setWeights(PX, PY, PZ, PF);
184 tracker.setAssignmentPrecision(AssignmentPrecision);
185
186 tracker.setThreadNumber(this->threadNumber_);
187 tracker.setDebugLevel(this->debugLevel_);
188
189 std::vector<ttk::DiagramType> persistenceDiagrams(fieldNumber);
191 (int)fieldNumber, persistenceDiagrams, triangulation);
192
193 this->printMsg("Diagram computed", 1, t.getElapsedTime(), threadNumber_);
194 double previousStepTime = t.getElapsedTime();
195
196 std::vector<std::vector<ttk::MatchingType>> maximaMatchings(fieldNumber - 1);
197 std::vector<std::vector<ttk::MatchingType>> sad_1_Matchings(fieldNumber - 1);
198 std::vector<std::vector<ttk::MatchingType>> sad_2_Matchings(fieldNumber - 1);
199 std::vector<std::vector<ttk::MatchingType>> minimaMatchings(fieldNumber - 1);
200
201 std::vector<std::vector<ttk::SimplexId>> maxMap(fieldNumber);
202 std::vector<std::vector<ttk::SimplexId>> sad_1Map(fieldNumber);
203 std::vector<std::vector<ttk::SimplexId>> sad_2Map(fieldNumber);
204 std::vector<std::vector<ttk::SimplexId>> minMap(fieldNumber);
205
206 tracker.performMatchings(persistenceDiagrams, maximaMatchings,
207 sad_1_Matchings, sad_2_Matchings, minimaMatchings,
208 maxMap, sad_1Map, sad_2Map, minMap);
209
210 this->printMsg("Matchings computed", 1, t.getElapsedTime() - previousStepTime,
212 previousStepTime = t.getElapsedTime();
213
214 vtkNew<vtkPoints> const points{};
215 vtkNew<vtkUnstructuredGrid> const outputMesh{};
216
217 vtkNew<vtkDoubleArray> costs{};
218 vtkNew<vtkDoubleArray> averagePersistences{};
219 vtkNew<vtkDoubleArray> integratedPersistences{};
220 vtkNew<vtkDoubleArray> maximalPersistences{};
221 vtkNew<vtkDoubleArray> minimalPersistences{};
222 vtkNew<vtkDoubleArray> instantPersistences{};
223 vtkNew<vtkDoubleArray> valueScalars{};
224 vtkNew<vtkIntArray> globalVertexIds{};
225 vtkNew<vtkIntArray> lengthScalars{};
226 vtkNew<vtkIntArray> timeScalars{};
227 vtkNew<vtkIntArray> connectedComponentIds{};
228 vtkNew<vtkIntArray> pointsCriticalType{};
229
230 costs->SetName("Costs");
231 averagePersistences->SetName("AveragePersistence");
232 integratedPersistences->SetName("IntegratedPersistence");
233 maximalPersistences->SetName("MaximalPersistence");
234 minimalPersistences->SetName("MinimalPersistence");
235 instantPersistences->SetName("InstantPersistence");
236 valueScalars->SetName("Scalar");
237 globalVertexIds->SetName("VertexGlobalId");
238 lengthScalars->SetName("ComponentLength");
239 timeScalars->SetName("TimeStep");
240 connectedComponentIds->SetName("ConnectedComponentId");
241 pointsCriticalType->SetName("CriticalType");
242
243 std::vector<ttk::trackingTuple> allTrackings;
244 std::vector<std::vector<double>> allTrackingsCosts;
245 std::vector<std::vector<double>> allTrackingsInstantPersistence;
246
247 unsigned int typesArrayLimits[3] = {};
248
249 tracker.performTrackings(
250 persistenceDiagrams, maximaMatchings, sad_1_Matchings, sad_2_Matchings,
251 minimaMatchings, maxMap, sad_1Map, sad_2Map, minMap, allTrackings,
252 allTrackingsCosts, allTrackingsInstantPersistence, typesArrayLimits);
253
254 this->printMsg("Trackings computed", 1, t.getElapsedTime() - previousStepTime,
256 previousStepTime = t.getElapsedTime();
257
258 double const spacing = Spacing;
259 bool const useGeometricSpacing = UseGeometricSpacing;
260
262 triangulation, allTrackings, allTrackingsCosts,
263 allTrackingsInstantPersistence, useGeometricSpacing, spacing, points,
264 outputMesh, pointsCriticalType, timeScalars, lengthScalars, globalVertexIds,
265 connectedComponentIds, costs, averagePersistences, integratedPersistences,
266 maximalPersistences, minimalPersistences, instantPersistences,
267 typesArrayLimits);
268
269 this->printMsg(
270 "Mesh built", 1, t.getElapsedTime() - previousStepTime, threadNumber_);
271 this->printMsg("Total run time ", 1, t.getElapsedTime(), this->threadNumber_);
272
273 output->ShallowCopy(outputMesh);
274
275 return 1;
276}
277
278template <class dataType, class triangulationType>
279int ttkTrackingFromFields::applyPostProcessing(
280 vtkUnstructuredGrid *output,
281 vtkDataSet *segOutput,
282 vtkDataSet *input,
283 const std::vector<vtkDataArray *> &inputScalarFields,
284 const triangulationType *triangulation) {
285
286 ttk::Timer timer;
288
289 const bool rebuildMesh = (DoLinearize || DoFusion);
290 if(!rebuildMesh && !DoMergeTree) {
291 this->printMsg("Nothing to do: all post-processing flags disabled.");
292 return 1;
293 }
294
295 vtkIntArray *compIdArray = vtkIntArray::SafeDownCast(
296 output->GetCellData()->GetArray("ConnectedComponentId"));
297 vtkIntArray *timeArray
298 = vtkIntArray::SafeDownCast(output->GetPointData()->GetArray("TimeStep"));
299 vtkIntArray *vertexGlobalIdArray = vtkIntArray::SafeDownCast(
300 output->GetPointData()->GetArray("VertexGlobalId"));
301 vtkIntArray *criticalTypeArray = vtkIntArray::SafeDownCast(
302 output->GetPointData()->GetArray("CriticalType"));
303
304 if(!compIdArray || !timeArray || !vertexGlobalIdArray) {
305 this->printErr("Tracking mesh is missing "
306 "ConnectedComponentId/TimeStep/VertexGlobalId; "
307 "skipping post-processing.");
308 return 0;
309 }
310
311 const vtkIdType numCells = output->GetNumberOfCells();
312 std::map<int, std::vector<vtkIdType>> cellsByTraj;
313 for(vtkIdType cellId = 0; cellId < numCells; ++cellId)
314 cellsByTraj[compIdArray->GetValue(cellId)].push_back(cellId);
315
316 const int numTraj = static_cast<int>(cellsByTraj.size());
317 std::vector<int> originalCCIds(numTraj, -1);
318 std::vector<std::vector<int>> trajTime(numTraj);
319 std::vector<std::vector<int>> trajVertexId(numTraj);
320 std::vector<std::vector<double>> trajX, trajY;
321 std::vector<int> trajCriticalType(numTraj, -1);
322 if(rebuildMesh) {
323 trajX.assign(numTraj, {});
324 trajY.assign(numTraj, {});
325 }
326
327 vtkNew<vtkIdList> cellPointIds;
328 auto collectUniqueSortedPointIds = [&](const std::vector<vtkIdType> &cellIds,
329 std::vector<vtkIdType> &pointIds) {
330 pointIds.clear();
331 pointIds.reserve(cellIds.size() * 2);
332 for(const vtkIdType cId : cellIds) {
333 cellPointIds->Reset();
334 output->GetCellPoints(cId, cellPointIds);
335 const vtkIdType n = cellPointIds->GetNumberOfIds();
336 for(vtkIdType k = 0; k < n; ++k)
337 pointIds.push_back(cellPointIds->GetId(k));
338 }
339 std::sort(pointIds.begin(), pointIds.end());
340 pointIds.erase(
341 std::unique(pointIds.begin(), pointIds.end()), pointIds.end());
342 std::sort(pointIds.begin(), pointIds.end(), [&](vtkIdType a, vtkIdType b) {
343 return timeArray->GetValue(a) < timeArray->GetValue(b);
344 });
345 };
346
347 size_t tIdx = 0;
348 for(const auto &kv : cellsByTraj) {
349 originalCCIds[tIdx] = kv.first;
350 std::vector<vtkIdType> pointIds;
351 collectUniqueSortedPointIds(kv.second, pointIds);
352
353 auto &ts = trajTime[tIdx];
354 auto &vid = trajVertexId[tIdx];
355 ts.reserve(pointIds.size());
356 vid.reserve(pointIds.size());
357 if(rebuildMesh) {
358 trajX[tIdx].reserve(pointIds.size());
359 trajY[tIdx].reserve(pointIds.size());
360 }
361
362 double xyz[3];
363 for(const vtkIdType pId : pointIds) {
364 ts.push_back(timeArray->GetValue(pId));
365 vid.push_back(vertexGlobalIdArray->GetValue(pId));
366 if(rebuildMesh) {
367 output->GetPoint(pId, xyz);
368 trajX[tIdx].push_back(xyz[0]);
369 trajY[tIdx].push_back(xyz[1]);
370 }
371 }
372 if(criticalTypeArray && !pointIds.empty())
373 trajCriticalType[tIdx] = criticalTypeArray->GetValue(pointIds.front());
374 ++tIdx;
375 }
376
377 ttk::TrackingPostProcessing ppt;
379 ppt.setDebugLevel(this->debugLevel_);
380
381 ppt.setDoLinearize(DoLinearize);
382 ppt.setDoFusion(DoFusion);
383 ppt.setDoLinearizeFuse(LinearizeFuse);
384 ppt.setDoMergeTree(DoMergeTree);
385 ppt.setUseSplitTree(UseSplitTree);
386
387 ppt.setCosCol(std::cos(CosColDegrees * M_PI / 180.0));
388 ppt.setMaxRadius(MaxLinkRadius);
389 ppt.setMaxFrameDist(MaxFrameDist);
390 ppt.setPersistenceThreshold(Tolerance);
391 ppt.setMaxSurfSize(MaxSurfSize);
392 ppt.setUseOtsuSimplification(UseOtsuSimplification);
393 ppt.setOtsuBins(OtsuBins);
394
395 double *bounds = input->GetBounds();
396 ppt.setBoundaryXMin(bounds[0]);
397 ppt.setBoundaryXMax(bounds[1]);
398 ppt.setBoundaryYMin(bounds[2]);
399 ppt.setBoundaryYMax(bounds[3]);
400
401 ppt.preconditionTriangulation(const_cast<triangulationType *>(triangulation));
402
403 if(DoMergeTree) {
404 std::vector<void *> inputFields;
405 inputFields.reserve(inputScalarFields.size());
406 for(vtkDataArray *a : inputScalarFields)
407 inputFields.push_back(ttkUtils::GetVoidPointer(a));
408 ppt.setInputScalars(inputFields);
409 }
410
411 std::vector<std::vector<int>> vertexTrajPerFrame;
412
413 // merge-tree segmentation only
414 if(!rebuildMesh) {
415 std::vector<ttk::TrackingPostProcessing::LinearTrajectory> rawTraj;
416 rawTraj.reserve(numTraj);
417 for(int i = 0; i < numTraj; ++i) {
418 if(trajTime[i].empty())
419 continue;
420 ttk::TrackingPostProcessing::LinearTrajectory lt{};
421 lt.isLinearized = false;
422 lt.startFrame = trajTime[i].front();
423 lt.endFrame = trajTime[i].back();
424 lt.finalChainId = originalCCIds[i];
425 lt.originalTrajId = originalCCIds[i];
426 lt.criticalPoints.reserve(trajTime[i].size());
427 for(size_t k = 0; k < trajTime[i].size(); ++k)
428 lt.criticalPoints.emplace_back(
429 trajTime[i][k], static_cast<ttk::SimplexId>(trajVertexId[i][k]));
430 rawTraj.push_back(std::move(lt));
431 }
432
433 std::vector<double> surfMin, surfMax, surfMean;
434 const int mtStatus = ppt.computeMergeTree<dataType, triangulationType>(
435 triangulation, rawTraj, surfMin, surfMax, surfMean, vertexTrajPerFrame);
436 if(mtStatus < 0) {
437 this->printWrn("Merge-tree segmentation failed; "
438 "keeping the raw tracking mesh.");
439 return 0;
440 }
441
442 writeSegmentationArrays(segOutput, vertexTrajPerFrame);
443
444 this->printMsg("Post-processing (merge-tree only, "
445 + std::to_string(rawTraj.size()) + " trajectories)",
446 1.0, timer.getElapsedTime(), this->threadNumber_);
448 return 1;
449 }
450
451 // full postprocess pipeline (+ optional merge-tree)
452 std::vector<ttk::TrackingPostProcessing::LinearTrajectory> linearTraj;
453 std::vector<ttk::TrackingPostProcessing::LinearTrajectory> finalTraj;
454 std::vector<ttk::TrackingPostProcessing::FuseRecord> fuseRecords;
455 std::vector<double> surfMin, surfMax, surfMean;
456
457 const int status = ppt.execute<dataType, triangulationType>(
458 trajTime, trajVertexId, trajX, trajY, trajCriticalType, linearTraj,
459 finalTraj, fuseRecords, surfMin, surfMax, surfMean, vertexTrajPerFrame,
460 triangulation);
461 if(status != 1) {
462 this->printWrn("Post-processing returned non-success status; "
463 "keeping the raw tracking mesh.");
464 return 0;
465 }
466
467 const vtkIdType nOut = static_cast<vtkIdType>(finalTraj.size());
468
469 int maxChainId = -1;
470 for(const auto &c : finalTraj) {
471 if(c.finalChainId > maxChainId)
472 maxChainId = c.finalChainId;
473 }
474 const int nChains = maxChainId + 1;
475 std::vector<int> chainCriticalType(std::max(nChains, 0), -1);
476 for(size_t i = 0; i < linearTraj.size() && i < trajCriticalType.size(); ++i) {
477 const int cid = linearTraj[i].finalChainId;
478 if(cid >= 0 && cid < nChains && chainCriticalType[cid] < 0)
479 chainCriticalType[cid] = trajCriticalType[i];
480 }
481
482 vtkNew<vtkUnstructuredGrid> newGrid{};
483 vtkNew<vtkPoints> newPoints{};
484 vtkNew<vtkCellArray> newLines{};
485 newPoints->SetNumberOfPoints(2 * nOut);
486
487 auto makeIntArr = [](const char *name, vtkIdType n) {
488 auto a = vtkSmartPointer<vtkIntArray>::New();
489 a->SetName(name);
490 a->SetNumberOfTuples(n);
491 return a;
492 };
493 auto makeDblArr = [](const char *name, vtkIdType n) {
494 auto a = vtkSmartPointer<vtkDoubleArray>::New();
495 a->SetName(name);
496 a->SetNumberOfTuples(n);
497 return a;
498 };
499
500 auto trajIdArr = makeIntArr("TrajId", nOut);
501 auto startFrameArr = makeIntArr("StartFrame", nOut);
502 auto endFrameArr = makeIntArr("EndFrame", nOut);
503 auto durationArr = makeIntArr("Duration", nOut);
504 auto criticalTypeOut = makeIntArr("CriticalType", nOut);
505 auto axArr = makeDblArr("ax", nOut);
506 auto bxArr = makeDblArr("bx", nOut);
507 auto ayArr = makeDblArr("ay", nOut);
508 auto byArr = makeDblArr("by", nOut);
509 auto surfMinArr = makeDblArr("SegmentationMin", nOut);
510 auto surfMaxArr = makeDblArr("SegmentationMax", nOut);
511 auto surfMeanArr = makeDblArr("SegmentationMean", nOut);
512 vtkSmartPointer<vtkIntArray> compIdOut;
513 if(!LinearizeFuse)
514 compIdOut = makeIntArr("ConnectedComponentId", nOut);
515
516 for(vtkIdType i = 0; i < nOut; ++i) {
517 const auto &c = finalTraj[i];
518
519 double x0, y0, x1, y1;
520 int sF;
521 if(DoStartFrame && (DoLinearize || LinearizeFuse)) {
522 sF = StartFrame;
523 } else {
524 sF = c.startFrame;
525 }
526 const int eF = c.endFrame;
527 if(DoLinearize) {
528 x0 = c.evalX(sF);
529 y0 = c.evalY(sF);
530 x1 = c.evalX(eF);
531 y1 = c.evalY(eF);
532 } else if(!c.criticalPoints.empty()) {
533 x0 = c.evalX(sF);
534 y0 = c.evalY(sF);
535 x1 = c.evalX(eF);
536 y1 = c.evalY(eF);
537 const ttk::SimplexId v0 = c.criticalPoints.front().second;
538 const ttk::SimplexId v1 = c.criticalPoints.back().second;
539 if(v0 >= 0 && v0 < triangulation->getNumberOfVertices()) {
540 float a, b, cZ;
541 triangulation->getVertexPoint(v0, a, b, cZ);
542 x0 = a;
543 y0 = b;
544 }
545 if(v1 >= 0 && v1 < triangulation->getNumberOfVertices()) {
546 float a, b, cZ;
547 triangulation->getVertexPoint(v1, a, b, cZ);
548 x1 = a;
549 y1 = b;
550 }
551 } else {
552 x0 = c.evalX(sF);
553 y0 = c.evalY(sF);
554 x1 = c.evalX(eF);
555 y1 = c.evalY(eF);
556 }
557
558 const vtkIdType p0 = 2 * i + 0;
559 const vtkIdType p1 = 2 * i + 1;
560 const double spacing = Spacing;
561
562 newPoints->SetPoint(p0, x0, y0, static_cast<double>(sF * spacing));
563 newPoints->SetPoint(p1, x1, y1, static_cast<double>(eF * spacing));
564
565 vtkNew<vtkLine> line{};
566 line->GetPointIds()->SetId(0, p0);
567 line->GetPointIds()->SetId(1, p1);
568 newLines->InsertNextCell(line);
569
570 trajIdArr->SetValue(i, c.finalChainId);
571 startFrameArr->SetValue(i, c.startFrame);
572 endFrameArr->SetValue(i, eF);
573 durationArr->SetValue(i, (eF - c.startFrame) + 1);
574 {
575 const int cid = c.finalChainId;
576 const int t = (cid >= 0 && cid < nChains) ? chainCriticalType[cid] : -1;
577 criticalTypeOut->SetValue(i, t);
578 }
579 axArr->SetValue(i, c.ax);
580 bxArr->SetValue(i, c.bx);
581 ayArr->SetValue(i, c.ay);
582 byArr->SetValue(i, c.by);
583 surfMinArr->SetValue(i, surfMin[i]);
584 surfMaxArr->SetValue(i, surfMax[i]);
585 surfMeanArr->SetValue(i, surfMean[i]);
586 if(compIdOut)
587 compIdOut->SetValue(i, c.originalTrajId);
588 }
589
590 newGrid->SetPoints(newPoints);
591 newGrid->SetCells(VTK_LINE, newLines);
592 newGrid->GetCellData()->AddArray(trajIdArr);
593 if(compIdOut)
594 newGrid->GetCellData()->AddArray(compIdOut);
595 newGrid->GetCellData()->AddArray(startFrameArr);
596 newGrid->GetCellData()->AddArray(endFrameArr);
597 newGrid->GetCellData()->AddArray(durationArr);
598 newGrid->GetCellData()->AddArray(criticalTypeOut);
599 newGrid->GetCellData()->AddArray(axArr);
600 newGrid->GetCellData()->AddArray(bxArr);
601 newGrid->GetCellData()->AddArray(ayArr);
602 newGrid->GetCellData()->AddArray(byArr);
603 if(DoMergeTree) {
604 newGrid->GetCellData()->AddArray(surfMinArr);
605 newGrid->GetCellData()->AddArray(surfMaxArr);
606 newGrid->GetCellData()->AddArray(surfMeanArr);
607 }
608
609 output->ShallowCopy(newGrid);
610
611 if(DoMergeTree)
612 writeSegmentationArrays(segOutput, vertexTrajPerFrame);
613
615 return 1;
616}
617
618void ttkTrackingFromFields::writeSegmentationArrays(
619 vtkDataSet *segOutput,
620 const std::vector<std::vector<int>> &vertexTrajPerFrame) {
621
622 if(vertexTrajPerFrame.empty())
623 return;
624
625 const vtkIdType nPts = segOutput->GetNumberOfPoints();
626 const int nFrames = static_cast<int>(vertexTrajPerFrame.size());
627
628 for(int frame = 0; frame < nFrames; ++frame) {
629 const auto &labels = vertexTrajPerFrame[frame];
630 if(static_cast<vtkIdType>(labels.size()) != nPts) {
631 this->printWrn("Segmentation output size mismatch on frame "
632 + std::to_string(frame));
633 continue;
634 }
635 char segName[20];
636 std::snprintf(segName, sizeof(segName), "Seg_%04d", frame);
637 vtkNew<vtkIntArray> segArr;
638 segArr->SetName(segName);
639 segArr->SetNumberOfComponents(1);
640 segArr->SetNumberOfTuples(nPts);
641 for(vtkIdType v = 0; v < nPts; ++v)
642 segArr->SetValue(v, labels[v]);
643 segOutput->GetPointData()->AddArray(segArr);
644 }
645}
646
648 vtkInformationVector **inputVector,
649 vtkInformationVector *outputVector) {
650
651 auto input = vtkDataSet::GetData(inputVector[0]);
652 auto output = vtkUnstructuredGrid::GetData(outputVector, 0);
653 auto segOutput = vtkDataSet::GetData(outputVector, 1);
655 if(!triangulation)
656 return 0;
657
658 this->preconditionTriangulation(triangulation);
659
660 if(input == nullptr || output == nullptr || segOutput == nullptr) {
661 return -1;
662 }
663 segOutput->ShallowCopy(input);
664 std::vector<vtkDataArray *> inputScalarFieldsRaw;
665 std::vector<vtkDataArray *> inputScalarFields;
666 const auto pointData = input->GetPointData();
667 int numberOfInputFields = pointData->GetNumberOfArrays();
668 if(numberOfInputFields < 3) {
669 this->printErr("Not enough input fields to perform tracking.");
670 }
671
672 vtkDataArray *firstScalarField = pointData->GetArray(0);
673
674 for(int i = 0; i < numberOfInputFields; ++i) {
675 vtkDataArray *currentScalarField = pointData->GetArray(i);
676 if(currentScalarField == nullptr
677 || currentScalarField->GetName() == nullptr) {
678 continue;
679 }
680 std::string const sfname{currentScalarField->GetName()};
681 if(sfname.rfind("_Order") == (sfname.size() - 6)) {
682 continue;
683 }
684 if(firstScalarField->GetDataType() != currentScalarField->GetDataType()) {
685 this->printErr("Inconsistent field data type or size between fields `"
686 + std::string{firstScalarField->GetName()} + "' and `"
687 + sfname + "'");
688 return -1;
689 }
690 inputScalarFieldsRaw.push_back(currentScalarField);
691 }
692
693 std::sort(inputScalarFieldsRaw.begin(), inputScalarFieldsRaw.end(),
694 [](vtkDataArray *a, vtkDataArray *b) {
695 std::string s1 = a->GetName();
696 std::string s2 = b->GetName();
697 return std::lexicographical_compare(
698 s1.begin(), s1.end(), s2.begin(), s2.end());
699 });
700
701 numberOfInputFields = inputScalarFieldsRaw.size();
702 int const end = EndTimestep <= 0 ? numberOfInputFields
703 : std::min(numberOfInputFields, EndTimestep);
704 for(int i = StartTimestep; i < end; i += Sampling) {
705 vtkDataArray *currentScalarField = inputScalarFieldsRaw[i];
706 // Print scalar field names:
707 // std::cout << currentScalarField->GetName() << std::endl;
708 inputScalarFields.push_back(currentScalarField);
709 }
710
711 // Input -> persistence filter.
712 std::string const algorithm = DistanceAlgorithm;
713 int const pvalg = PVAlgorithm;
714 bool useTTKMethod = false;
715 bool trackWithCriticalPoints = (pvalg == 2);
716
717 if(pvalg >= 0) {
718 switch(pvalg) {
719 case 0:
720 case 1:
721 case 2:
722 case 3:
723 useTTKMethod = true;
724 break;
725 case 4:
726 break;
727 default:
728 this->printMsg("Unrecognized tracking method.");
729 break;
730 }
731 } else {
732 using ttk::str2int;
733 switch(str2int(algorithm.c_str())) {
734 case str2int("0"):
735 case str2int("ttk"):
736 case str2int("1"):
737 case str2int("legacy"):
738 case str2int("2"):
739 case str2int("geometric"):
740 case str2int("3"):
741 case str2int("parallel"):
742 useTTKMethod = true;
743 break;
744 case str2int("4"):
745 case str2int("greedy"):
746 break;
747 default:
748 this->printMsg("Unrecognized tracking method.");
749 break;
750 }
751 }
752
753 // 0. get data
754 int const fieldNumber = inputScalarFields.size();
755 std::vector<void *> inputFields(fieldNumber);
756 for(int i = 0; i < fieldNumber; i++) {
757 inputFields[i] = ttkUtils::GetVoidPointer(inputScalarFields[i]);
758 }
759 this->setInputScalars(inputFields);
760
761 // 0'. get offsets
762 std::vector<ttk::SimplexId *> inputOrders(fieldNumber);
763 for(int i = 0; i < fieldNumber; ++i) {
764 this->SetInputArrayToProcess(0, 0, 0, 0, inputScalarFields[i]->GetName());
765 auto orderArray
766 = this->GetOrderArray(input, 0, triangulation, false, 0, false);
767 inputOrders[i]
768 = static_cast<ttk::SimplexId *>(ttkUtils::GetVoidPointer(orderArray));
769 }
770 this->setInputOffsets(inputOrders);
771
772 int status = 0;
773 this->printMsg("Tracking trajectories over " + std::to_string(fieldNumber)
774 + " timesteps");
775 if(useTTKMethod && !trackWithCriticalPoints) {
777 inputScalarFields[0]->GetDataType(), triangulation->getType(),
778 (status = this->trackWithPersistenceMatching<VTK_TT, TTK_TT>(
779 output, fieldNumber, (TTK_TT *)triangulation->getData())));
780 } else if(useTTKMethod && trackWithCriticalPoints) {
782 inputScalarFields[0]->GetDataType(), triangulation->getType(),
783 (status = this->trackWithCriticalPointMatching<VTK_TT, TTK_TT>(
784 output, fieldNumber, (TTK_TT *)triangulation->getData())));
785 } else {
786 this->printMsg("The specified matching method is not supported.");
787 }
788
789 if(status == 1 && EnablePostProc) {
790 ttkVtkTemplateMacro(inputScalarFields[0]->GetDataType(),
791 triangulation->getType(),
792 (this->applyPostProcessing<VTK_TT, TTK_TT>(
793 output, segOutput, input, inputScalarFields,
794 (TTK_TT *)triangulation->getData())));
795 }
796 return status;
797}
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition BaseClass.h:47
#define M_PI
Definition Os.h:50
ttk::Triangulation * GetTriangulation(vtkDataSet *dataSet)
vtkDataArray * GetOrderArray(vtkDataSet *const inputData, const int scalarArrayIdx, ttk::Triangulation *triangulation, const bool getGlobalOrder=false, const int orderArrayIdx=0, const bool enforceOrderArrayIdx=false)
TTK VTK-filter that takes an input time-varying data set (represented by a list of scalar fields) and...
int FillOutputPortInformation(int port, vtkInformation *info) override
int RequestDataObject(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
int FillInputPortInformation(int port, vtkInformation *info) override
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
static int buildMeshAlt(const triangulationType *triangulation, const std::vector< ttk::trackingTuple > &trackings, const std::vector< std::vector< double > > &allTrackingsCosts, const std::vector< std::vector< double > > &allTrackingsInstantPersistence, const bool useGeometricSpacing, const double spacing, vtkPoints *points, vtkUnstructuredGrid *outputMesh, vtkIntArray *pointsCriticalType, vtkIntArray *timeScalars, vtkIntArray *lengthScalars, vtkIntArray *globalVertexIds, vtkIntArray *connectedComponentIds, vtkDoubleArray *costs, vtkDoubleArray *averagePersistence, vtkDoubleArray *integratedPersistence, vtkDoubleArray *maximalPersistence, vtkDoubleArray *minimalPersistence, vtkDoubleArray *instantPersistence, unsigned int *sizes)
static int buildMesh(const std::vector< ttk::trackingTuple > &trackings, const std::vector< std::vector< ttk::MatchingType > > &outputMatchings, const std::vector< ttk::DiagramType > &inputPersistenceDiagrams, const bool useGeometricSpacing, const double spacing, const bool doPostProc, const std::vector< std::set< int > > &trackingTupleToMerged, vtkPoints *points, vtkUnstructuredGrid *persistenceDiagram, vtkDoubleArray *persistenceScalars, vtkDoubleArray *valueScalars, vtkIntArray *matchingIdScalars, vtkIntArray *lengthScalars, vtkIntArray *timeScalars, vtkIntArray *componentIds, vtkIntArray *pointTypeScalars, const ttk::Debug &dbg)
static void * GetVoidPointer(vtkDataArray *array, vtkIdType start=0)
Definition ttkUtils.cpp:228
virtual int setThreadNumber(const int threadNumber)
Definition BaseClass.h:80
int debugLevel_
Definition Debug.h:379
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:159
virtual int setDebugLevel(const int &debugLevel)
Definition Debug.cpp:147
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
double getElapsedTime()
Definition Timer.h:15
void setWeights(double PX, double PY, double PZ, double PF)
void performMatchings(const std::vector< DiagramType > &persistenceDiagrams, std::vector< std::vector< MatchingType > > &maximaMatchings, std::vector< std::vector< MatchingType > > &sad_1_Matchings, std::vector< std::vector< MatchingType > > &sad_2_Matchings, std::vector< std::vector< MatchingType > > &minimaMatchings, std::vector< std::vector< SimplexId > > &maxMap, std::vector< std::vector< SimplexId > > &sad_1Map, std::vector< std::vector< SimplexId > > &sad_2Map, std::vector< std::vector< SimplexId > > &minMap)
void performTrackings(const std::vector< DiagramType > &persistenceDiagrams, const std::vector< std::vector< MatchingType > > &maximaMatchings, const std::vector< std::vector< MatchingType > > &sad_1_Matchings, const std::vector< std::vector< MatchingType > > &sad_2_Matchings, const std::vector< std::vector< MatchingType > > &minimaMatchings, const std::vector< std::vector< SimplexId > > &maxMap, const std::vector< std::vector< SimplexId > > &sad_1Map, const std::vector< std::vector< SimplexId > > &sad_2Map, const std::vector< std::vector< SimplexId > > &minMap, std::vector< trackingTuple > &allTrackings, std::vector< std::vector< double > > &allTrackingsCost, std::vector< std::vector< double > > &allTrackingsInstantPersistences, unsigned int(&typesArrayLimits)[3])
void setInputOffsets(std::vector< SimplexId * > &io)
void preconditionTriangulation(AbstractTriangulation *triangulation)
void setInputScalars(std::vector< void * > &is)
int performDiagramComputation(int fieldNumber, std::vector< ttk::DiagramType > &persistenceDiagrams, const triangulationType *triangulation)
int performMatchings(int numInputs, std::vector< ttk::DiagramType > &inputPersistenceDiagrams, std::vector< std::vector< MatchingType > > &outputMatchings, const std::string &algorithm, const std::string &wasserstein, double tolerance, double px, double py, double pz, double ps, double pe)
int performTracking(std::vector< ttk::DiagramType > &allDiagrams, std::vector< std::vector< MatchingType > > &allMatchings, std::vector< trackingTuple > &trackings)
int performPostProcess(const std::vector< ttk::DiagramType > &allDiagrams, const std::vector< trackingTuple > &trackings, std::vector< std::set< int > > &trackingTupleToMerged, const double postProcThresh)
int execute(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 > &finalTraj, std::vector< FuseRecord > &fuseRecords, std::vector< double > &surfMin, std::vector< double > &surfMax, std::vector< double > &surfMean, std::vector< std::vector< int > > &vertexTrajPerFrame, const triangulationType *triangulation)
correctTrajectory + computeMergeTree.
void setInputScalars(const std::vector< void * > &inputScalars)
int preconditionTriangulation(ttk::AbstractTriangulation *triangulation) const
int computeMergeTree(const triangulationType *triangulation, const std::vector< LinearTrajectory > &finalTraj, std::vector< double > &surfMin, std::vector< double > &surfMax, std::vector< double > &surfMean, std::vector< std::vector< int > > &vertexTrajPerFrame)
Compute merge-tree-based segmentation per trajectory && per frame.
Triangulation is a class that provides time and memory efficient traversal methods on triangulations ...
AbstractTriangulation * getData()
Triangulation::Type getType() const
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
constexpr unsigned long long str2int(const char *str, int h=0)
T end(std::pair< T, T > &p)
Definition ripser.cpp:503
std::vector< std::pair< int, ttk::SimplexId > > criticalPoints
#define ttkVtkTemplateMacro(dataType, triangulationType, call)
Definition ttkMacros.h:69
vtkStandardNewMacro(ttkTrackingFromFields)
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)