Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpMunkres.h
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Class for Munkres Assignment Algorithm.
32 */
33
34#pragma once
35
36#include <visp3/core/vpConfig.h>
37
38// Check if std:c++17 or higher.
39// Here we cannot use (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17) in the declaration of the class
40#if ((__cplusplus >= 201703L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)))
41
42// System
43#include <optional>
44#include <tuple>
45
46// Internal
47#include "vpMath.h"
48
65class VISP_EXPORT vpMunkres
66{
67public:
68 template <typename Type>
69 static std::vector<std::pair<unsigned int, unsigned int> > run(std::vector<std::vector<Type> > costs);
70
71private:
72 enum ZERO_T : unsigned int;
73 enum STEP_T : unsigned int;
74
75 // Init
76 template <typename Type> static void padCostMatrix(std::vector<std::vector<Type> > &costs);
77
78 // Global helpers
79 template <typename Type>
80 static std::optional<std::pair<unsigned int, unsigned int> > findAZero(const std::vector<std::vector<Type> > &costs,
81 const std::vector<bool> &row_cover,
82 const std::vector<bool> &col_cover);
83 static std::optional<unsigned int> findStarInRow(const std::vector<std::vector<ZERO_T> > &mask,
84 const unsigned int &row);
85 static std::optional<unsigned int> findStarInCol(const std::vector<std::vector<ZERO_T> > &mask,
86 const unsigned int &col);
87 static std::optional<unsigned int> findPrimeInRow(const std::vector<std::vector<ZERO_T> > &mask,
88 const unsigned int &row);
89 template <typename Type>
90 static Type findSmallest(const std::vector<std::vector<Type> > &costs, const std::vector<bool> &row_cover,
91 const std::vector<bool> &col_cover);
92
93 // FSM helpers
94 static void augmentPath(std::vector<std::vector<ZERO_T> > &mask,
95 const std::vector<std::pair<unsigned int, unsigned int> > &path);
96 static void clearCovers(std::vector<bool> &row_cover, std::vector<bool> &col_cover);
97 static void erasePrimes(std::vector<std::vector<ZERO_T> > &mask);
98
99 // FSM
100 template <typename Type> static STEP_T stepOne(std::vector<std::vector<Type> > &costs);
101 template <typename Type>
102 static STEP_T stepTwo(std::vector<std::vector<Type> > &costs, std::vector<std::vector<ZERO_T> > &mask,
103 std::vector<bool> &row_cover, std::vector<bool> &col_cover);
104 static STEP_T stepThree(const std::vector<std::vector<ZERO_T> > &mask, std::vector<bool> &col_cover);
105 template <typename Type>
106 static std::tuple<STEP_T, std::optional<std::pair<unsigned int, unsigned int> > >
107 stepFour(const std::vector<std::vector<Type> > &costs, std::vector<std::vector<ZERO_T> > &mask,
108 std::vector<bool> &row_cover, std::vector<bool> &col_cover);
109 static STEP_T stepFive(std::vector<std::vector<ZERO_T> > &mask, const std::pair<unsigned int, unsigned int> &path_0,
110 std::vector<bool> &row_cover, std::vector<bool> &col_cover);
111 template <typename Type>
112 static STEP_T stepSix(std::vector<std::vector<Type> > &costs, const std::vector<bool> &row_cover,
113 const std::vector<bool> &col_cover);
114
115private:
116 static constexpr auto ZeroEpsilon { 1e-6 };
117};
118
119enum vpMunkres::ZERO_T : unsigned int { NA = 0, STARRED = 1, PRIMED = 2 };
120
121enum vpMunkres::STEP_T : unsigned int { ENTRY = 0, ONE = 1, TWO = 2, THREE = 3, FOUR = 4, FIVE = 5, SIX = 6, DONE };
122
128template <typename Type> inline void vpMunkres::padCostMatrix(std::vector<std::vector<Type> > &costs)
129{
130 const auto row_input_size = costs.size();
131 const auto col_input_size = costs.at(0).size();
132
133 if (row_input_size > col_input_size) {
134 for (auto &vec : costs)
135 vec.resize(row_input_size, 0);
136 }
137
138 while (costs.size() < col_input_size) {
139 costs.emplace_back(col_input_size, 0);
140 }
141}
142
151template <typename Type>
152inline std::optional<std::pair<unsigned int, unsigned int> >
153vpMunkres::findAZero(const std::vector<std::vector<Type> > &costs, const std::vector<bool> &row_cover,
154 const std::vector<bool> &col_cover)
155{
156 for (auto row = 0u; row < costs.size(); row++)
157 for (auto col = 0u; col < costs.size(); col++)
158 if (vpMath::equal(costs.at(row).at(col), static_cast<Type>(vpMunkres::ZeroEpsilon)) && !row_cover.at(row) &&
159 !col_cover.at(col)) {
160 return std::make_optional<std::pair<unsigned int, unsigned int> >(row, col);
161 }
162
163 return std::nullopt;
164}
165
174template <typename Type>
175inline Type vpMunkres::findSmallest(const std::vector<std::vector<Type> > &costs, const std::vector<bool> &row_cover,
176 const std::vector<bool> &col_cover)
177{
178 auto minval = std::numeric_limits<Type>::max();
179 for (auto row = 0u; row < costs.size(); row++)
180 for (auto col = 0u; col < costs.size(); col++)
181 if (minval > costs.at(row).at(col) && !row_cover.at(row) && !col_cover.at(col)) {
182 minval = costs.at(row).at(col);
183 }
184
185 return minval;
186}
187
196template <typename Type> inline vpMunkres::STEP_T vpMunkres::stepOne(std::vector<std::vector<Type> > &costs)
197{
198 // process rows
199 std::for_each(begin(costs), end(costs), [](auto &cost_row) {
200 const auto min_in_row = *std::min_element(begin(cost_row), end(cost_row));
201 std::transform(begin(cost_row), end(cost_row), begin(cost_row),
202 [&min_in_row](auto &cost) { return cost - min_in_row; });
203 });
204
205 // process cols
206 for (auto col = 0u; col < costs.size(); ++col) {
207 auto minval = std::numeric_limits<Type>::max();
208 for (const auto &cost_row : costs) {
209 minval = std::min<Type>(minval, cost_row.at(col));
210 }
211
212 for (auto &cost_row : costs) {
213 cost_row.at(col) -= minval;
214 }
215 }
216
217 return vpMunkres::STEP_T(2);
218}
219
231template <typename Type>
232inline vpMunkres::STEP_T vpMunkres::stepTwo(std::vector<std::vector<Type> > &costs,
233 std::vector<std::vector<vpMunkres::ZERO_T> > &mask,
234 std::vector<bool> &row_cover, std::vector<bool> &col_cover)
235{
236 for (auto row = 0u; row < costs.size(); row++) {
237 for (auto col = 0u; col < costs.size(); col++) {
238 if (vpMath::equal(costs.at(row).at(col), static_cast<Type>(vpMunkres::ZeroEpsilon)) && !row_cover.at(row) &&
239 !col_cover.at(col)) {
240 mask.at(row).at(col) = vpMunkres::ZERO_T::STARRED;
241 row_cover.at(row) = true;
242 col_cover.at(col) = true;
243 break;
244 }
245 }
246 }
247
248 clearCovers(row_cover, col_cover);
249 return vpMunkres::STEP_T(3);
250}
251
264template <typename Type>
265inline std::tuple<vpMunkres::STEP_T, std::optional<std::pair<unsigned int, unsigned int> > >
266vpMunkres::stepFour(const std::vector<std::vector<Type> > &costs, std::vector<std::vector<vpMunkres::ZERO_T> > &mask,
267 std::vector<bool> &row_cover, std::vector<bool> &col_cover)
268{
269 if (const auto zero = findAZero(costs, row_cover, col_cover)) {
270 const auto [row, col] = *zero; // convenient zero.value() is not working on iOS
271 mask.at(row).at(col) = vpMunkres::ZERO_T::PRIMED;
272
273 if (const auto star_in_row = findStarInRow(mask, row)) {
274 row_cover.at(row) = true;
275 col_cover.at(*star_in_row) = false;
276 return { vpMunkres::STEP_T(4), std::nullopt }; // Repeat
277 }
278 else {
279 return { vpMunkres::STEP_T(5), std::make_optional<std::pair<unsigned int, unsigned int> >(row, col) };
280 }
281 }
282 else {
283 return { vpMunkres::STEP_T(6), std::nullopt };
284 }
285}
286
296template <typename Type>
297inline vpMunkres::STEP_T vpMunkres::stepSix(std::vector<std::vector<Type> > &costs, const std::vector<bool> &row_cover,
298 const std::vector<bool> &col_cover)
299{
300 const auto minval = findSmallest(costs, row_cover, col_cover);
301 for (auto row = 0u; row < costs.size(); row++) {
302 for (auto col = 0u; col < costs.size(); col++) {
303 if (row_cover.at(row)) {
304 costs.at(row).at(col) += minval;
305 }
306
307 if (!col_cover.at(col)) {
308 costs.at(row).at(col) -= minval;
309 }
310 }
311 }
312
313 return vpMunkres::STEP_T(4);
314}
315
322template <typename Type>
323inline std::vector<std::pair<unsigned int, unsigned int> > vpMunkres::run(std::vector<std::vector<Type> > costs)
324{
325 const auto original_row_size = static_cast<Type>(costs.size());
326 const auto original_col_size = static_cast<Type>(costs.front().size());
327 const size_t sq_size = static_cast<size_t>(std::max<Type>(original_row_size, original_col_size));
328
329 auto mask = std::vector<std::vector<vpMunkres::ZERO_T> >(sq_size, std::vector<vpMunkres::ZERO_T>(sq_size, vpMunkres::ZERO_T::NA));
330 auto row_cover = std::vector<bool>(sq_size, false);
331 auto col_cover = std::vector<bool>(sq_size, false);
332
333 std::optional<std::pair<unsigned int, unsigned int> > path_0 { std::nullopt };
334
335 auto step { vpMunkres::STEP_T::ENTRY };
336 while (step != vpMunkres::STEP_T::DONE) {
337 switch (step) {
338 case vpMunkres::STEP_T::ENTRY:
339 padCostMatrix(costs);
340 step = vpMunkres::STEP_T(1);
341 break;
342 case 1:
343 step = stepOne(costs);
344 break;
345 case 2:
346 step = stepTwo(costs, mask, row_cover, col_cover);
347 break;
348 case 3:
349 step = stepThree(mask, col_cover);
350 break;
351 case 4:
352 std::tie(step, path_0) = stepFour(costs, mask, row_cover, col_cover);
353 break;
354 case 5:
355 step = stepFive(mask, *path_0, row_cover, col_cover);
356 break;
357 case 6:
358 step = stepSix(costs, row_cover, col_cover);
359 break;
360 case vpMunkres::STEP_T::DONE:
361 default:
362 break;
363 }
364 }
365
366 // Compute the pairs
367 std::vector<std::pair<unsigned int, unsigned int> > ret {};
368 for (auto i = 0u; i < original_row_size; i++) {
369 if (const auto it = std::find(begin(mask.at(i)), end(mask.at(i)), vpMunkres::ZERO_T::STARRED);
370 it != end(mask.at(i))) {
371 if (const unsigned int j = static_cast<unsigned int>(std::distance(begin(mask.at(i)), it));
372 j < original_col_size) {
373 ret.emplace_back(i, j);
374 }
375 }
376 }
377
378 return ret;
379}
380END_VISP_NAMESPACE
381#endif
static bool equal(double x, double y, double threshold=0.001)
Definition vpMath.h:470
static std::vector< std::pair< unsigned int, unsigned int > > run(std::vector< std::vector< Type > > costs)
Definition vpMunkres.h:323