TTK
Loading...
Searching...
No Matches
MorphologicalOperators.h
Go to the documentation of this file.
1
12
13#pragma once
14
15// base code includes
16#include <Debug.h>
17#include <Triangulation.h>
18
19#include <array>
20#include <limits>
21
22namespace ttk {
23
24 class MorphologicalOperators : public virtual Debug {
25 public:
27 this->setDebugMsgPrefix("MorphologicalOperators");
28 }
29
30 ~MorphologicalOperators() override = default;
31
33 ttk::AbstractTriangulation *triangulation) const {
34 return triangulation->preconditionVertexNeighbors();
35 }
36
37 template <class DT, class TT = ttk::AbstractTriangulation>
38 int execute(
39 // Output
40 DT *outputLabels,
41
42 // Input
43 const int &mode,
44 const int &iterations,
45 const bool grayscale,
46 const DT *inputLabels,
47 const DT &pivotLabel,
48 TT *triangulation) const;
49
50 template <class DT, class TT = ttk::AbstractTriangulation>
52 // Output
53 DT *outputLabels,
54
55 // Input
56 const int &mode,
57 const int &iterations,
58 const bool grayscale,
59 const DT *inputLabels,
60 const DT &pivotLabel,
61 TT *triangulation) const;
62 };
63} // namespace ttk
64
65template <class DT, class TT>
67 // Output
68 DT *outputLabels,
69
70 // Input
71 const int &mode,
72 const int &iterations,
73 const bool grayscale,
74 const DT *inputLabels,
75 const DT &pivotLabel,
76 TT *triangulation) const {
77
78 if(mode < 2) {
79 // erosion or dilation
80 return this->performElementaryMorphoOp(outputLabels, mode, iterations,
81 grayscale, inputLabels, pivotLabel,
82 triangulation);
83 } else {
84 std::array<int, 2> ops{};
85 if(mode == 2) {
86 // morphological opening
87 ops = {1, 0}; // erosion then dilation
88 } else if(mode == 3) {
89 // morphological closing
90 ops = {0, 1}; // dilation then erosion
91 } else {
92 this->printErr("Invalid morphological operation requested");
93 return 0;
94 }
95
96 std::vector<DT> tmp(triangulation->getNumberOfVertices());
97 const auto status = this->performElementaryMorphoOp(
98 tmp.data(), ops[0], iterations, grayscale, inputLabels, pivotLabel,
99 triangulation);
100 if(status != 1) {
101 return status;
102 }
103 return this->performElementaryMorphoOp(outputLabels, ops[1], iterations,
104 grayscale, tmp.data(), pivotLabel,
105 triangulation);
106 }
107}
108
109template <class DT, class TT>
111 // Output
112 DT *outputLabels,
113
114 // Input
115 const int &mode,
116 const int &iterations,
117 const bool grayscale,
118 const DT *inputLabels,
119 const DT &pivotLabel,
120 TT *triangulation) const {
121
122 SimplexId const nVertices = triangulation->getNumberOfVertices();
123
124 std::vector<DT> temp;
125 if(iterations > 1) {
126 Timer t;
127 this->printMsg("Allocating temporary memory", 0, 0, this->threadNumber_,
129 temp.resize(nVertices);
130 this->printMsg("Allocating temporary memory", 1, t.getElapsedTime(),
131 this->threadNumber_);
132 }
133
134 std::string const msg = std::string(mode == 0 ? "Dilating " : "Eroding ")
135 + std::to_string(iterations) + "x value "
136 + std::to_string(pivotLabel);
137
138 this->printMsg(msg, 0, 0, this->threadNumber_, debug::LineMode::REPLACE);
139
140 Timer t;
141 for(int it = 0; it < iterations; it++) {
142
143 const DT *source = it == 0 ? inputLabels
144 : (iterations + it) % 2 == 0 ? outputLabels
145 : temp.data();
146 DT *target = (iterations + it) % 2 == 0 ? temp.data() : outputLabels;
147
148 // NOTE: Directly dilating a vertex value to all its neighbors requires
149 // parallel write locks, so instead focusing on vertices that need to
150 // update their value optimizes parallel efficiency.
151 if(!grayscale) {
152 if(mode == 0) { // binary dilation
153#ifdef TTK_ENABLE_OPENMP
154#pragma omp parallel for num_threads(this->threadNumber_)
155#endif
156 for(SimplexId i = 0; i < nVertices; i++) {
157 // first copy data
158 target[i] = source[i];
159
160 // if current vertex value is not a dilated value
161 if(source[i] != pivotLabel) {
162 // check neighbors if they need to be dilated
163 const SimplexId nNeighbors
164 = triangulation->getVertexNeighborNumber(i);
165 SimplexId nIndex{-1};
166 for(SimplexId n = 0; n < nNeighbors; n++) {
167 triangulation->getVertexNeighbor(i, n, nIndex);
168 if(source[nIndex] == pivotLabel) {
169 target[i] = source[nIndex];
170 break;
171 }
172 }
173 }
174 }
175 } else { // binary erosion
176 const DT minLabel = std::numeric_limits<DT>::min();
177
178#ifdef TTK_ENABLE_OPENMP
179#pragma omp parallel for num_threads(this->threadNumber_)
180#endif
181 for(SimplexId i = 0; i < nVertices; i++) {
182 // first copy data
183 target[i] = source[i];
184
185 // if current vertex value needs to be eroded
186 if(source[i] == pivotLabel) {
187 // check neighbors if neighbors have a non-eroded label
188 const SimplexId nNeighbors
189 = triangulation->getVertexNeighborNumber(i);
190 SimplexId nIndex{-1};
191 DT maxNeighborLabel = minLabel;
192 for(SimplexId n = 0; n < nNeighbors; n++) {
193 triangulation->getVertexNeighbor(i, n, nIndex);
194 if(source[nIndex] != pivotLabel
195 && maxNeighborLabel < source[nIndex]) {
196 maxNeighborLabel = source[nIndex];
197 }
198 }
199 if(maxNeighborLabel != minLabel)
200 target[i] = maxNeighborLabel;
201 }
202 }
203 }
204 } else {
205
206 if(mode == 0) { // grayscale dilation
207#ifdef TTK_ENABLE_OPENMP
208#pragma omp parallel for num_threads(this->threadNumber_)
209#endif // TTK_ENABLE_OPENMP
210 for(SimplexId i = 0; i < nVertices; i++) {
211 // first copy data
212 target[i] = source[i];
213 const auto nNeighs = triangulation->getVertexNeighborNumber(i);
214 for(SimplexId n = 0; n < nNeighs; n++) {
215 SimplexId neigh{};
216 triangulation->getVertexNeighbor(i, n, neigh);
217 target[i] = std::max(target[i], source[neigh]);
218 }
219 }
220 } else { // grayscale erosion
221#ifdef TTK_ENABLE_OPENMP
222#pragma omp parallel for num_threads(this->threadNumber_)
223#endif // TTK_ENABLE_OPENMP
224 for(SimplexId i = 0; i < nVertices; i++) {
225 // first copy data
226 target[i] = source[i];
227 const auto nNeighs = triangulation->getVertexNeighborNumber(i);
228 for(SimplexId n = 0; n < nNeighs; n++) {
229 SimplexId neigh{};
230 triangulation->getVertexNeighbor(i, n, neigh);
231 target[i] = std::min(target[i], source[neigh]);
232 }
233 }
234 }
235 }
236
237 this->printMsg(msg, (float)it / (float)(iterations - 1), t.getElapsedTime(),
238 this->threadNumber_, debug::LineMode::REPLACE);
239 }
240
241 this->printMsg(msg, 1, t.getElapsedTime(), this->threadNumber_);
242
243 return 1;
244}
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
Minimalist debugging class.
Definition Debug.h:88
void setDebugMsgPrefix(const std::string &prefix)
Definition Debug.h:364
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
TTK morphologicalOperators processing package.
int preconditionTriangulation(ttk::AbstractTriangulation *triangulation) const
int performElementaryMorphoOp(DT *outputLabels, const int &mode, const int &iterations, const bool grayscale, const DT *inputLabels, const DT &pivotLabel, TT *triangulation) const
~MorphologicalOperators() override=default
int execute(DT *outputLabels, const int &mode, const int &iterations, const bool grayscale, const DT *inputLabels, const DT &pivotLabel, TT *triangulation) const
double getElapsedTime()
Definition Timer.h:15
The Topology ToolKit.
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)