DGtal 2.1.1
Loading...
Searching...
No Matches
ParallelIIEstimator.ih
1/**
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 **/
16
17/**
18 * @file ParallelIIEstimator.ih
19 * @author Bastien Doignies
20 * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), INSA-Lyon, France
21 *
22 * @date 2026/05/01
23 *
24 * Header file for module ParallelEstimator.ih
25 *
26 * This file is part of the DGtal library.
27 */
28
29
30#include <deque>
31#include <optional>
32#include <ranges>
33
34namespace DGtal
35{
36 template<class TEstimator, typename TSplitter>
37 template<class... Args>
38 ParallelIIEstimator<TEstimator, TSplitter>::
39 ParallelIIEstimator(Splitter splitter, int32_t nbThread, Args&&... args)
40 : mySplitter(std::move(splitter))
41 {
42 if (nbThread <= 0)
43 nbThread = std::max(1, omp_get_max_threads());
44
45 // Note: We can not use std::vector constructor or resize
46 // because it will copy the estimator but it can have shared
47 // state.
48 // We rather build a new one in a loop by calling the constructor
49 // implictely with emplace_back
50 myEstimators.reserve(nbThread);
51 for (int i = 0; i < nbThread; ++i)
52 myEstimators.emplace_back(std::forward<Args>(args)...);
53 }
54
55 template<class TEstimator, typename TSplitter>
56 void ParallelIIEstimator<TEstimator, TSplitter>::clear()
57 {
58 for (auto& estim : myEstimators)
59 estim.clear();
60 }
61
62 template<class TEstimator, typename TSplitter>
63 typename ParallelIIEstimator<TEstimator, TSplitter>::Scalar
64 ParallelIIEstimator<TEstimator, TSplitter>::h() const { return myEstimators[0].h(); }
65
66 template<class TEstimator, typename TSplitter>
67 void ParallelIIEstimator<TEstimator, TSplitter>::attach(
68 ConstAlias<KSpace> K,
69 ConstAlias<PointPredicate> pp)
70 {
71 clear();
72
73 myKSpace = K;
74 myPointPredicate = pp;
75 }
76
77 template<class TEstimator, typename TSplitter>
78 void ParallelIIEstimator<TEstimator, TSplitter>::setParams(double dRadius)
79 {
80 myRadius = dRadius;
81 }
82
83 template<class TEstimator, typename TSplitter>
84 bool ParallelIIEstimator<TEstimator, TSplitter>::isValid() const
85 {
86 bool valid = true;
87 for (const auto& estim : myEstimators)
88 valid = valid && estim.isValid();
89
90 return valid;
91 }
92
93 template<class TEstimator, typename TSplitter>
94 template<typename ItA, typename ItB>
95 void ParallelIIEstimator<TEstimator, TSplitter>::init(double h_, ItA, ItB)
96 {
97 myH = h_;
98 }
99
100 template<class TEstimator, typename TSplitter>
101 template<typename It>
102 typename ParallelIIEstimator<TEstimator, TSplitter>::Quantity
103 ParallelIIEstimator<TEstimator, TSplitter>::eval(It it)
104 {
105 if (!myEstimators[0].isValid())
106 {
107 auto ite = it; ite++;
108 myEstimators[0].init(myH, it, ite);
109 }
110
111 return {
112 .location = *it,
113 .value = myEstimators[0].eval(it)
114 };
115 }
116
117 template<class TEstimator, typename TSplitter>
118 template<typename It, typename Oit>
119 Oit ParallelIIEstimator<TEstimator, TSplitter>::eval(It itb, It ite, Oit rslt)
120 {
121 Domain mainDomain(myKSpace->lowerBound(), myKSpace->upperBound());
122 std::vector<SplitInfo<Domain>> domains = mySplitter(mainDomain, myEstimators.size());
123
124 using Result = std::pair<Surfel, std::optional<EstimatorQuantity>>;
125 std::deque<Result> result;
126 std::vector<std::vector<Result*>> resultLocation(domains.size());
127 for ( auto it = itb; it != ite; ++it )
128 {
129 result.push_back( { *it, std::nullopt } );
130 Result* current = &result.back();
131
132 for (unsigned int j = 0; j < domains.size(); ++j)
133 {
134 // Should be true for only one of the domain. The break ensure
135 // each surfel belongs to one and only one domain, meaning there
136 // will be no concurrent write afterward
137 if (domains[j].domain.isInside(myKSpace->interiorVoxel(*it)))
138 {
139 resultLocation[j].push_back( current );
140 break;
141 }
142 }
143 }
144
145 #pragma omp parallel for
146 for (uint32_t i = 0; i < domains.size(); ++i)
147 {
148 auto surfels = resultLocation[i] | std::views::transform([](const auto* kv) -> const Surfel&
149 {
150 return kv->first;
151 });
152 auto values = resultLocation[i] | std::views::transform([](auto* kv) -> std::optional<EstimatorQuantity>&
153 {
154 return kv->second;
155 });
156
157 auto& estim = myEstimators[i];
158
159 estim.attach(*myKSpace, *myPointPredicate);
160 estim.setParams(myRadius);
161
162 estim.init(myH, surfels.begin(), surfels.end());
163 estim.eval(surfels.begin(), surfels.end(), values.begin());
164 }
165
166 for (const auto& entry : result)
167 {
168 assert(entry.second.has_value());
169 *rslt++ = *entry.second;
170 }
171
172 return rslt;
173 }
174
175 template<class TEstimator, typename TSplitter>
176 void
177 ParallelIIEstimator<TEstimator, TSplitter>::selfDisplay( std::ostream & out ) const
178 {
179 out << "[ParallelIIEstimator Estim=";
180 myEstimators[0].selfDisplay(out);
181 out << "]";
182 }
183}