Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpMbtTukeyEstimator.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 * Tukey M-estimator.
32 */
33
34#ifndef VP_MBT_TUKEY_ESTIMATOR_H
35#define VP_MBT_TUKEY_ESTIMATOR_H
36
37#include <vector>
38#include <visp3/core/vpConfig.h>
39#include <visp3/core/vpColVector.h>
40
41#ifndef DOXYGEN_SHOULD_SKIP_THIS
42
61 template <typename T> class vpMbtTukeyEstimator
62{
63public:
64 void MEstimator(const std::vector<T> &residues, std::vector<T> &weights, T NoiseThreshold);
65 void MEstimator(const vpColVector &residues, vpColVector &weights, double NoiseThreshold);
66
67private:
68 T getMedian(std::vector<T> &vec);
69 void MEstimator_impl(const std::vector<T> &residues, std::vector<T> &weights, T NoiseThreshold);
70 void MEstimator_impl_simd(const std::vector<T> &residues, std::vector<T> &weights, T NoiseThreshold);
71 void psiTukey(const T sig, std::vector<T> &x, std::vector<T> &weights);
72 void psiTukey(const T sig, std::vector<T> &x, vpColVector &weights);
73
74 std::vector<T> m_normres;
75 std::vector<T> m_residues;
76};
77END_VISP_NAMESPACE
78#endif //#ifndef DOXYGEN_SHOULD_SKIP_THIS
79
80/*
81 * The code bellow previously in vpMbtTuckeyEstimator.cpp produced
82 * a link issue with MinGW-W64 x86_64-8.1.0-posix-seh-rt_v6-rev0 (g++ 8.1.0)
83 * libvisp_mbt.so.3.1.0: undefined reference to
84 * `vpMbtTukeyEstimator<double>::MEstimator(std::vector<double,
85 * std::allocator<double> > const&, std::vector<double, std::allocator<double>
86 * >&, double)'
87 * Note that with the previous MinGW-W64 version x86_64-7.3.0-posix-seh-rt_v6-rev0 (g++ 7.3.0)
88 * the build succeed.
89 *
90 * To remove this link issue the solution was to move the content of vpMbtTuckeyEstimator.cpp
91 * before remove.
92 */
93#include <algorithm>
94#include <cmath>
95#include <iostream>
96
97#include <visp3/core/vpCPUFeatures.h>
98
99#define USE_TRANSFORM 1
100#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) && USE_TRANSFORM
101#define HAVE_TRANSFORM 1
102#include <functional>
103#endif
104
105#if defined __SSE2__ || defined _M_X64 || (defined _M_IX86_FP && _M_IX86_FP >= 2)
106#include <emmintrin.h>
107#define VISP_HAVE_SSE2 1
108
109#if defined __SSE3__ || (defined _MSC_VER && _MSC_VER >= 1500)
110#include <pmmintrin.h>
111#define VISP_HAVE_SSE3 1
112#endif
113#if defined __SSSE3__ || (defined _MSC_VER && _MSC_VER >= 1500)
114#include <tmmintrin.h>
115#define VISP_HAVE_SSSE3 1
116#endif
117#endif
118
119#if defined _WIN32 && defined(_M_ARM64)
120# define _ARM64_DISTINCT_NEON_TYPES
121# include <Intrin.h>
122# include <arm_neon.h>
123# define VISP_HAVE_NEON 1
124#elif (defined(__ARM_NEON__) || defined (__ARM_NEON)) && defined(__aarch64__)
125# include <arm_neon.h>
126# define VISP_HAVE_NEON 1
127#endif
128
129#ifndef DOXYGEN_SHOULD_SKIP_THIS
130
131#if HAVE_TRANSFORM
132 namespace
133{
134// Check if std:c++14 or higher
135#if ((__cplusplus >= 201402L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L)))
136auto AbsDiff = [](const auto &a, const auto &b) { return std::fabs(a - b); };
137#else
138template <typename T> struct AbsDiff : public std::binary_function<T, T, T>
139{
140 T operator()(const T a, const T b) const { return std::fabs(a - b); }
141};
142#endif
143} // namespace
144#endif
145
147template class vpMbtTukeyEstimator<float>;
148template class vpMbtTukeyEstimator<double>;
149
150#if VISP_HAVE_SSSE3
151namespace
152{
153inline __m128 abs_ps(__m128 x)
154{
155 static const __m128 sign_mask = _mm_set1_ps(-0.f); // -0.f = 1 << 31
156 return _mm_andnot_ps(sign_mask, x);
157}
158} // namespace
159#endif
160
161template <typename T> T vpMbtTukeyEstimator<T>::getMedian(std::vector<T> &vec)
162{
163 // Not the exact median when even number of elements
164 size_t index = static_cast<size_t>(ceil(static_cast<double>(vec.size()) / 2.0)) - 1;
165 std::nth_element(vec.begin(), vec.begin() + static_cast<long long>(index), vec.end());
166 return vec[index];
167}
168
169// Without MEstimator_impl, error with g++4.6, ok with gcc 5.4.0
170// Ubuntu-12.04-Linux-i386-g++4.6-Dyn-RelWithDebInfo-dc1394-v4l2-X11-OpenCV2.3.1-lapack-gsl-Coin-jpeg-png-xml-pthread-OpenMP-dmtx-zbar-Wov-Weq-Moment:
171// libvisp_mbt.so.3.1.0: undefined reference to
172// `vpMbtTukeyEstimator<double>::MEstimator(std::vector<double,
173// std::allocator<double> > const&, std::vector<double, std::allocator<double>
174// >&, double)'
175template <typename T>
176void vpMbtTukeyEstimator<T>::MEstimator_impl(const std::vector<T> &residues, std::vector<T> &weights,
177 const T NoiseThreshold)
178{
179 if (residues.empty()) {
180 return;
181 }
182
183 m_residues = residues;
184
185 T med = getMedian(m_residues);
186 m_normres.resize(residues.size());
187
188#if HAVE_TRANSFORM
189// Check if std:c++14 or higher
190#if ((__cplusplus >= 201402L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L)))
191 std::transform(residues.begin(), residues.end(), m_normres.begin(), std::bind(AbsDiff, std::placeholders::_1, med));
192#else
193 std::transform(residues.begin(), residues.end(), m_normres.begin(),
194 std::bind(AbsDiff<T>(), std::placeholders::_1, med));
195#endif
196#else
197 for (size_t i = 0; i < m_residues.size(); i++) {
198 m_normres[i] = (std::fabs(residues[i] - med));
199 }
200#endif
201
202 m_residues = m_normres;
203 T normmedian = getMedian(m_residues);
204
205 // 1.48 keeps scale estimate consistent for a normal probability dist.
206 T sigma = static_cast<T>(1.4826) * normmedian; // median Absolute Deviation
207
208 // Set a minimum threshold for sigma
209 // (when sigma reaches the level of noise in the image)
210 if (sigma < NoiseThreshold) {
211 sigma = NoiseThreshold;
212 }
213
214 psiTukey(sigma, m_normres, weights);
215}
216
217template <>
218inline void vpMbtTukeyEstimator<float>::MEstimator_impl_simd(const std::vector<float> &residues,
219 std::vector<float> &weights,
220 float NoiseThreshold)
221{
222#if VISP_HAVE_SSSE3 || VISP_HAVE_NEON
223 if (residues.empty()) {
224 return;
225 }
226
227 m_residues = residues;
228
229 float med = getMedian(m_residues);
230 m_normres.resize(residues.size());
231
232 size_t i = 0;
233#if VISP_HAVE_SSSE3
234 __m128 med_128 = _mm_set_ps1(med);
235#else
236 float32x4_t med_128 = vdupq_n_f32(med);
237#endif
238
239 if (m_residues.size() >= 4) {
240 for (i = 0; i <= m_residues.size() - 4; i += 4) {
241#if VISP_HAVE_SSSE3
242 __m128 residues_128 = _mm_loadu_ps(residues.data() + i);
243 _mm_storeu_ps(m_normres.data() + i, abs_ps(_mm_sub_ps(residues_128, med_128)));
244#else
245 float32x4_t residues_128 = vld1q_f32(residues.data() + i);
246 vst1q_f32(m_normres.data() + i, vabsq_f32(vsubq_f32(residues_128, med_128)));
247#endif
248 }
249 }
250
251 for (; i < m_residues.size(); i++) {
252 m_normres[i] = (std::fabs(residues[i] - med));
253 }
254
255 m_residues = m_normres;
256 float normmedian = getMedian(m_residues);
257
258 // 1.48 keeps scale estimate consistent for a normal probability dist.
259 float sigma = 1.4826f * normmedian; // median Absolute Deviation
260
261 // Set a minimum threshold for sigma
262 // (when sigma reaches the level of noise in the image)
263 if (sigma < NoiseThreshold) {
264 sigma = NoiseThreshold;
265 }
266
267 psiTukey(sigma, m_normres, weights);
268#else
269 (void)residues;
270 (void)weights;
271 (void)NoiseThreshold;
272#endif
273}
274
278template <>
279inline void vpMbtTukeyEstimator<double>::MEstimator_impl_simd(const std::vector<double> &residues,
280 std::vector<double> &weights,
281 double NoiseThreshold)
282{
283#if VISP_HAVE_SSSE3 || VISP_HAVE_NEON
284 if (residues.empty()) {
285 return;
286 }
287
288 m_residues = residues;
289
290 double med = getMedian(m_residues);
291 m_normres.resize(residues.size());
292
293#if HAVE_TRANSFORM
294// Check if std:c++14 or higher
295#if ((__cplusplus >= 201402L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L)))
296 std::transform(residues.begin(), residues.end(), m_normres.begin(), std::bind(AbsDiff, std::placeholders::_1, med));
297#else
298 std::transform(residues.begin(), residues.end(), m_normres.begin(),
299 std::bind(AbsDiff<double>(), std::placeholders::_1, med));
300#endif
301#else
302 for (size_t i = 0; i < m_residues.size(); i++) {
303 m_normres[i] = (std::fabs(residues[i] - med));
304 }
305#endif
306
307 m_residues = m_normres;
308 double normmedian = getMedian(m_residues);
309
310 // 1.48 keeps scale estimate consistent for a normal probability dist.
311 double sigma = 1.4826 * normmedian; // median Absolute Deviation
312
313 // Set a minimum threshold for sigma
314 // (when sigma reaches the level of noise in the image)
315 if (sigma < NoiseThreshold) {
316 sigma = NoiseThreshold;
317 }
318
319 psiTukey(sigma, m_normres, weights);
320#else
321 (void)residues;
322 (void)weights;
323 (void)NoiseThreshold;
324#endif
325}
326
330template <>
331inline void vpMbtTukeyEstimator<float>::MEstimator(const std::vector<float> &residues, std::vector<float> &weights,
332 float NoiseThreshold)
333{
334#if defined(VISP_HAVE_SIMDLIB)
336#else
337 bool checkSimd = vpCPUFeatures::checkSSSE3();
338#endif
339#if !VISP_HAVE_SSSE3 && !VISP_HAVE_NEON
340 checkSimd = false;
341#endif
342
343 if (checkSimd)
344 MEstimator_impl_simd(residues, weights, NoiseThreshold);
345 else
346 MEstimator_impl(residues, weights, NoiseThreshold);
347}
348
352template <>
353inline void vpMbtTukeyEstimator<double>::MEstimator(const std::vector<double> &residues, std::vector<double> &weights,
354 double NoiseThreshold)
355{
356#if defined(VISP_HAVE_SIMDLIB)
358#else
359 bool checkSimd = vpCPUFeatures::checkSSSE3();
360#endif
361#if !VISP_HAVE_SSSE3 && !VISP_HAVE_NEON
362 checkSimd = false;
363#endif
364
365 if (checkSimd)
366 MEstimator_impl_simd(residues, weights, NoiseThreshold);
367 else
368 MEstimator_impl(residues, weights, NoiseThreshold);
369}
370
374template <typename T> void vpMbtTukeyEstimator<T>::psiTukey(const T sig, std::vector<T> &x, vpColVector &weights)
375{
376 double C = static_cast<double>(sig) * 4.6851;
377
378 // Here we consider that sig cannot be equal to 0
379 for (unsigned int i = 0; i < static_cast<unsigned int>(x.size()); i++) {
380 double xi = static_cast<double>(x[i]) / C;
381 xi *= xi;
382
383 if (xi > 1.) {
384 weights[i] = 0;
385 }
386 else {
387 xi = 1 - xi;
388 xi *= xi;
389 weights[i] = xi;
390 }
391 }
392}
393
397template <>
398inline void vpMbtTukeyEstimator<double>::MEstimator(const vpColVector &residues, vpColVector &weights,
399 double NoiseThreshold)
400{
401 if (residues.size() == 0) {
402 return;
403 }
404
405 m_residues.resize(0);
406 m_residues.reserve(residues.size());
407 m_residues.insert(m_residues.end(), &residues.data[0], &residues.data[residues.size()]);
408
409 double med = getMedian(m_residues);
410
411 m_normres.resize(residues.size());
412 for (size_t i = 0; i < m_residues.size(); i++) {
413 m_normres[i] = std::fabs(residues[static_cast<unsigned int>(i)] - med);
414 }
415
416 m_residues = m_normres;
417 double normmedian = getMedian(m_residues);
418
419 // 1.48 keeps scale estimate consistent for a normal probability dist.
420 double sigma = 1.4826 * normmedian; // median Absolute Deviation
421
422 // Set a minimum threshold for sigma
423 // (when sigma reaches the level of noise in the image)
424 if (sigma < NoiseThreshold) {
425 sigma = NoiseThreshold;
426 }
427
428 psiTukey(sigma, m_normres, weights);
429}
430
434template <>
435inline void vpMbtTukeyEstimator<float>::MEstimator(const vpColVector &residues, vpColVector &weights,
436 double NoiseThreshold)
437{
438 if (residues.size() == 0) {
439 return;
440 }
441
442 m_residues.resize(0);
443 m_residues.reserve(residues.size());
444 for (unsigned int i = 0; i < residues.size(); i++) {
445 m_residues.push_back(static_cast<float>(residues[i]));
446 }
447
448 float med = getMedian(m_residues);
449
450 m_normres.resize(residues.size());
451 for (size_t i = 0; i < m_residues.size(); i++) {
452 m_normres[i] = static_cast<float>(std::fabs(static_cast<float>(residues[static_cast<unsigned int>(i)]) - med));
453 }
454
455 m_residues = m_normres;
456 float normmedian = getMedian(m_residues);
457
458 // 1.48 keeps scale estimate consistent for a normal probability dist.
459 float sigma = 1.4826f * normmedian; // median Absolute Deviation
460 float noise_threshold = static_cast<float>(NoiseThreshold);
461
462 // Set a minimum threshold for sigma
463 // (when sigma reaches the level of noise in the image)
464 if (sigma < noise_threshold) {
465 sigma = noise_threshold;
466 }
467
468 psiTukey(sigma, m_normres, weights);
469}
470
474template <class T> void vpMbtTukeyEstimator<T>::psiTukey(const T sig, std::vector<T> &x, std::vector<T> &weights)
475{
476 T C = static_cast<T>(4.6851) * sig;
477 weights.resize(x.size());
478 T one = static_cast<T>(1.);
479
480 // Here we consider that sig cannot be equal to 0
481 for (size_t i = 0; i < x.size(); i++) {
482 T xi = x[i] / C;
483 xi *= xi;
484
485 if (xi > one) {
486 weights[i] = 0;
487 }
488 else {
489 xi = 1 - xi;
490 xi *= xi;
491 weights[i] = xi;
492 }
493 }
494}
495END_VISP_NAMESPACE
496#endif //#ifndef DOXYGEN_SHOULD_SKIP_THIS
497
498#endif
Type * data
Address of the first element of the data array.
Definition vpArray2D.h:149
unsigned int size() const
Return the number of elements of the 2D array.
Definition vpArray2D.h:435
Implementation of column vector and the associated operations.
VISP_EXPORT bool checkSSSE3()
VISP_EXPORT bool checkNeon()