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 // TODO: Initialize properly the estimator (attaching shape, ...)
106 if (!myEstimators[0].isValid())
107 {
108 auto ite = it; ite++;
109 myEstimators[0].init(myH, it, ite);
110 }
111
112 return {
113 .location = *it,
114 .value = myEstimators[0].eval(it)
115 };
116 }
117
118 template<class TEstimator, typename TSplitter>
119 template<typename It, typename Oit>
120 Oit ParallelIIEstimator<TEstimator, TSplitter>::eval(It itb, It ite, Oit rslt)
121 {
122 Domain mainDomain(myKSpace->lowerBound(), myKSpace->upperBound());
123 std::vector<SplitInfo<Domain>> domains = mySplitter(mainDomain, myEstimators.size());
124
125 using Result = std::pair<Surfel, std::optional<EstimatorQuantity>>;
126 std::deque<Result> result;
127 std::vector<std::vector<Result*>> resultLocation(domains.size());
128 for ( auto it = itb; it != ite; ++it )
129 {
130 result.push_back( { *it, std::nullopt } );
131 Result* current = &result.back();
132
133 for (unsigned int j = 0; j < domains.size(); ++j)
134 {
135 // Should be true for only one of the domain. The break ensure
136 // each surfel belongs to one and only one domain, meaning there
137 // will be no concurrent write afterward
138 if (domains[j].domain.isInside(myKSpace->interiorVoxel(*it)))
139 {
140 resultLocation[j].push_back( current );
141 break;
142 }
143 }
144 }
145
146 #pragma omp parallel for
147 for (uint32_t i = 0; i < domains.size(); ++i)
148 {
149 auto surfels = resultLocation[i] | std::views::transform([](const auto* kv) -> const Surfel&
150 {
151 return kv->first;
152 });
153 auto values = resultLocation[i] | std::views::transform([](auto* kv) -> std::optional<EstimatorQuantity>&
154 {
155 return kv->second;
156 });
157
158 auto& estim = myEstimators[i];
159
160 estim.attach(*myKSpace, *myPointPredicate);
161 estim.setParams(myRadius);
162
163 estim.init(myH, surfels.begin(), surfels.end());
164 estim.eval(surfels.begin(), surfels.end(), values.begin());
165 }
166
167 for ( const auto& entry : result )
168 *rslt++ = *( entry.second );
169
170 return rslt;
171 }
172
173 template<class TEstimator, typename TSplitter>
174 void
175 ParallelIIEstimator<TEstimator, TSplitter>::selfDisplay( std::ostream & out ) const
176 {
177 out << "[ParallelIIEstimator Estim=";
178 myEstimators[0].selfDisplay(out);
179 out << "]";
180 }
181}