Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpMeSite.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 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  * Moving edges.
32  */
33 
39 #include <cmath> // std::fabs
40 #include <limits> // numeric_limits
41 #include <stdlib.h>
42 #include <visp3/core/vpTrackingException.h>
43 #include <visp3/me/vpMe.h>
44 #include <visp3/me/vpMeSite.h>
45 
47 
48 #ifndef DOXYGEN_SHOULD_SKIP_THIS
49 static bool horsImage(int i, int j, int half, int rows, int cols)
50 {
51  int half_1 = half + 1;
52  int half_3 = half + 3;
53  return ((0 < (half_1 - i)) || (((i - rows) + half_3) > 0) || (0 < (half_1 - j)) || (((j - cols) + half_3) > 0));
54 }
55 #endif
56 
58 {
59  // Site components
60  m_alpha = 0.0;
61  m_convlt = 0.0;
62  m_weight = -1;
63  m_contrastThreshold = 10000.0;
64 
65  m_selectDisplay = NONE;
66 
67  // Pixel components
68  m_i = 0;
69  m_j = 0;
70  m_ifloat = m_i;
71  m_jfloat = m_j;
72 
73  m_mask_sign = 1;
74 
75  m_normGradient = 0;
76 
77  m_state = NO_SUPPRESSION;
78 }
79 
81  : m_i(0), m_j(0), m_ifloat(0), m_jfloat(0), m_mask_sign(1), m_alpha(0.), m_convlt(0.), m_normGradient(0),
82  m_weight(1), m_contrastThreshold(10000.0), m_selectDisplay(NONE), m_state(NO_SUPPRESSION)
83 { }
84 
85 vpMeSite::vpMeSite(const double &ip, const double &jp)
86  : m_i(0), m_j(0), m_ifloat(0), m_jfloat(0), m_mask_sign(1), m_alpha(0.), m_convlt(0.), m_normGradient(0),
87  m_weight(1), m_contrastThreshold(10000.0), m_selectDisplay(NONE), m_state(NO_SUPPRESSION)
88 {
89  m_i = vpMath::round(ip);
90  m_j = vpMath::round(jp);
91  m_ifloat = ip;
92  m_jfloat = jp;
93 }
94 
96  : m_i(0), m_j(0), m_ifloat(0), m_jfloat(0), m_mask_sign(1), m_alpha(0.), m_convlt(0.), m_normGradient(0),
97  m_weight(1), m_contrastThreshold(10000.0), m_selectDisplay(NONE), m_state(NO_SUPPRESSION)
98 {
99  *this = mesite;
100 }
101 
102 // More an Update than init
103 // For points in meter form (to avoid approximations)
104 void vpMeSite::init(const double &ip, const double &jp, const double &alphap)
105 {
106  // Note: keep old value of m_convlt, contrast and threshold
107  m_selectDisplay = NONE;
108 
109  m_ifloat = ip;
110  m_i = vpMath::round(ip);
111  m_jfloat = jp;
112  m_j = vpMath::round(jp);
113  m_alpha = alphap;
114  m_mask_sign = 1;
115 }
116 
117 // initialise with convolution()
118 void vpMeSite::init(const double &ip, const double &jp, const double &alphap, const double &convltp)
119 {
120  m_selectDisplay = NONE;
121  m_ifloat = ip;
122  m_i = static_cast<int>(ip);
123  m_jfloat = jp;
124  m_j = static_cast<int>(jp);
125  m_alpha = alphap;
126  m_convlt = convltp;
127  m_mask_sign = 1;
128 }
129 
130 // initialise with convolution and sign
131 void vpMeSite::init(const double &ip, const double &jp, const double &alphap, const double &convltp, const int &sign)
132 {
133  m_selectDisplay = NONE;
134  m_ifloat = ip;
135  m_i = static_cast<int>(ip);
136  m_jfloat = jp;
137  m_j = static_cast<int>(jp);
138  m_alpha = alphap;
139  m_convlt = convltp;
140  m_mask_sign = sign;
141 }
142 
143 // initialise with convolution and sign
144 void vpMeSite::init(const double &ip, const double &jp, const double &alphap, const double &convltp, const int &sign, const double &contrastThreshold)
145 {
146  m_selectDisplay = NONE;
147  m_ifloat = ip;
148  m_i = static_cast<int>(ip);
149  m_jfloat = jp;
150  m_j = static_cast<int>(jp);
151  m_alpha = alphap;
152  m_convlt = convltp;
153  m_mask_sign = sign;
154  m_contrastThreshold = contrastThreshold;
155 }
156 
158 {
159  m_i = m.m_i;
160  m_j = m.m_j;
161  m_ifloat = m.m_ifloat;
162  m_jfloat = m.m_jfloat;
164  m_alpha = m.m_alpha;
165  m_convlt = m.m_convlt;
167  m_weight = m.m_weight;
169  m_selectDisplay = m.m_selectDisplay;
170  m_state = m.m_state;
171 
172  return *this;
173 }
174 
175 vpMeSite *vpMeSite::getQueryList(const vpImage<unsigned char> &I, const int &range) const
176 {
177  unsigned int range_ = static_cast<unsigned int>(range);
178  // Size of query list includes the point on the line
179  vpMeSite *list_query_pixels = new vpMeSite[(2 * range_) + 1];
180 
181  // range : +/- the range within which the pixel's
182  // correspondent will be sought
183 
184  double salpha = sin(m_alpha);
185  double calpha = cos(m_alpha);
186  int n = 0;
187  vpImagePoint ip;
188 
189  for (int k = -range; k <= range; ++k) {
190  double ii = m_ifloat + (k * salpha);
191  double jj = m_jfloat + (k * calpha);
192 
193  // Display
194  if ((m_selectDisplay == RANGE_RESULT) || (m_selectDisplay == RANGE)) {
195  ip.set_i(ii);
196  ip.set_j(jj);
198  }
199 
200  // Copy parent's convolution
201  vpMeSite pel;
203  pel.setDisplay(m_selectDisplay); // Display
204 
205  // Add site to the query list
206  list_query_pixels[n] = pel;
207  ++n;
208  }
209 
210  return list_query_pixels;
211 }
212 
213 // Specific function for ME
215 {
216  int half;
217  int height_ = static_cast<int>(I.getHeight());
218  int width_ = static_cast<int>(I.getWidth());
219 
220  double conv = 0.0;
221  unsigned int msize = me->getMaskSize();
222  half = static_cast<int>((msize - 1) >> 1);
223 
224  if (horsImage(m_i, m_j, half + me->getStrip(), height_, width_)) {
225  conv = 0.0;
226  m_i = 0;
227  m_j = 0;
228  }
229  else {
230  // Calculate tangent angle from normal
231  double theta = m_alpha + (M_PI / 2);
232  // Move tangent angle to within 0->M_PI for a positive
233  // mask index
234  while (theta < 0) {
235  theta += M_PI;
236  }
237  while (theta > M_PI) {
238  theta -= M_PI;
239  }
240 
241  // Convert radians to degrees
242  int thetadeg = vpMath::round((theta * 180) / M_PI);
243 
244  const int flatAngle = 180;
245  if (abs(thetadeg) == flatAngle) {
246  thetadeg = 0;
247  }
248 
249  unsigned int index_mask = static_cast<unsigned int>(thetadeg / static_cast<double>(me->getAngleStep()));
250 
251  unsigned int i_ = static_cast<unsigned int>(m_i);
252  unsigned int j_ = static_cast<unsigned int>(m_j);
253  unsigned int half_ = static_cast<unsigned int>(half);
254 
255  unsigned int ihalf = i_ - half_;
256  unsigned int jhalf = j_ - half_;
257 
258  for (unsigned int a = 0; a < msize; ++a) {
259  unsigned int ihalfa = ihalf + a;
260  for (unsigned int b = 0; b < msize; ++b) {
261  conv += m_mask_sign * me->getMask()[index_mask][a][b] * I(ihalfa, jhalf + b);
262  }
263  }
264  }
265 
266  return conv;
267 }
268 
269 void vpMeSite::track(const vpImage<unsigned char> &I, const vpMe *me, const bool &test_contrast)
270 {
271  int max_rank = -1;
272  double max_convolution = 0;
273  double max = 0;
274  double contrast = 0;
275 
276  // range = +/- range of pixels within which the correspondent
277  // of the current pixel will be sought
278  unsigned int range = me->getRange();
279 
280  vpMeSite *list_query_pixels = getQueryList(I, static_cast<int>(range));
281 
282  double contrast_max = 1 + me->getMu2();
283  double contrast_min = 1 - me->getMu1();
284 
285  // array in which likelihood ratios will be stored
286  double *likelihood = new double[(2 * range) + 1];
287  const unsigned int val_2 = 2;
288 
289  if (test_contrast) {
290  double diff = 1e6;
291  for (unsigned int n = 0; n < ((val_2 * range) + 1); ++n) {
292  // convolution results
293  double convolution_ = list_query_pixels[n].convolution(I, me);
294  double threshold = list_query_pixels[n].getContrastThreshold();
295 
297  threshold = 2.0 * threshold;
298  }
299  else {
300  double n_d = me->getMaskSize();
301  threshold = threshold / (100.0 * n_d * trunc(n_d / 2.0));
302  }
303 
304  // luminance ratio of reference pixel to potential correspondent pixel
305  // the luminance must be similar, hence the ratio value should
306  // lay between, for instance, 0.5 and 1.5 (parameter tolerance)
307  likelihood[n] = fabs(convolution_ + m_convlt);
308 
309  if (likelihood[n] > threshold) {
310  contrast = convolution_ / m_convlt;
311  if ((contrast > contrast_min) && (contrast < contrast_max) && (fabs(1 - contrast) < diff)) {
312  diff = fabs(1 - contrast);
313  max_convolution = convolution_;
314  max = likelihood[n];
315  max_rank = static_cast<int>(n);
316  }
317  }
318  }
319  }
320  else { // test on contrast only
321  for (unsigned int n = 0; n < ((val_2 * range) + 1); ++n) {
322  double threshold = list_query_pixels[n].getContrastThreshold();
323 
325  threshold = 2.0 * threshold;
326  }
327  else {
328  double n_d = me->getMaskSize();
329  threshold = threshold / (100.0 * n_d * trunc(n_d / 2.0));
330  }
331 
332  // convolution results
333  double convolution_ = list_query_pixels[n].convolution(I, me);
334  likelihood[n] = fabs(val_2 * convolution_);
335  if ((likelihood[n] > max) && (likelihood[n] > threshold)) {
336  max_convolution = convolution_;
337  max = likelihood[n];
338  max_rank = static_cast<int>(n);
339  }
340  }
341  }
342 
343  vpImagePoint ip;
344 
345  if (max_rank >= 0) {
346  if ((m_selectDisplay == RANGE_RESULT) || (m_selectDisplay == RESULT)) {
347  ip.set_i(list_query_pixels[max_rank].m_i);
348  ip.set_j(list_query_pixels[max_rank].m_j);
350  }
351 
352  *this = list_query_pixels[max_rank]; // The vpMeSite2 is replaced by the
353  // vpMeSite2 of max likelihood
354  m_normGradient = vpMath::sqr(max_convolution);
355 
356  m_convlt = max_convolution;
357 
358  delete[] list_query_pixels;
359  delete[] likelihood;
360  }
361  else // none of the query sites is better than the threshold
362  {
363  if ((m_selectDisplay == RANGE_RESULT) || (m_selectDisplay == RESULT)) {
364  ip.set_i(list_query_pixels[0].m_i);
365  ip.set_j(list_query_pixels[0].m_j);
367  }
368  m_normGradient = 0;
369  if (std::fabs(contrast) > std::numeric_limits<double>::epsilon()) {
370  m_state = CONTRAST; // contrast suppression
371  }
372  else {
373  m_state = THRESHOLD; // threshold suppression
374  }
375 
376  delete[] list_query_pixels;
377  delete[] likelihood; // modif portage
378  }
379 }
380 
381 int vpMeSite::operator!=(const vpMeSite &m) { return ((m.m_i != m_i) || (m.m_j != m_j)); }
382 
384 
386 
387 // Static functions
388 
389 void vpMeSite::display(const vpImage<unsigned char> &I, const double &i, const double &j, const vpMeSiteState &state)
390 {
391  const unsigned int crossSize = 3;
392  switch (state) {
393  case NO_SUPPRESSION:
394  vpDisplay::displayCross(I, vpImagePoint(i, j), crossSize, vpColor::green, 1);
395  break;
396 
397  case CONTRAST:
398  vpDisplay::displayCross(I, vpImagePoint(i, j), crossSize, vpColor::blue, 1);
399  break;
400 
401  case THRESHOLD:
402  vpDisplay::displayCross(I, vpImagePoint(i, j), crossSize, vpColor::purple, 1);
403  break;
404 
405  case M_ESTIMATOR:
406  vpDisplay::displayCross(I, vpImagePoint(i, j), crossSize, vpColor::red, 1);
407  break;
408 
409  case OUTSIDE_ROI_MASK:
410  vpDisplay::displayCross(I, vpImagePoint(i, j), crossSize, vpColor::cyan, 1);
411  break;
412 
413  default:
414  vpDisplay::displayCross(I, vpImagePoint(i, j), crossSize, vpColor::yellow, 1);
415  }
416 }
417 
418 void vpMeSite::display(const vpImage<vpRGBa> &I, const double &i, const double &j, const vpMeSiteState &state)
419 {
420  const unsigned int cross_size = 3;
421  switch (state) {
422  case NO_SUPPRESSION:
423  vpDisplay::displayCross(I, vpImagePoint(i, j), cross_size, vpColor::green, 1);
424  break;
425 
426  case CONTRAST:
427  vpDisplay::displayCross(I, vpImagePoint(i, j), cross_size, vpColor::blue, 1);
428  break;
429 
430  case THRESHOLD:
431  vpDisplay::displayCross(I, vpImagePoint(i, j), cross_size, vpColor::purple, 1);
432  break;
433 
434  case M_ESTIMATOR:
435  vpDisplay::displayCross(I, vpImagePoint(i, j), cross_size, vpColor::red, 1);
436  break;
437 
438  case OUTSIDE_ROI_MASK:
439  vpDisplay::displayCross(I, vpImagePoint(i, j), cross_size, vpColor::cyan, 1);
440  break;
441 
442  default:
443  vpDisplay::displayCross(I, vpImagePoint(i, j), cross_size, vpColor::yellow, 1);
444  }
445 }
446 
447 VISP_EXPORT std::ostream &operator<<(std::ostream &os, vpMeSite &vpMeS)
448 {
449  return (os << "Alpha: " << vpMeS.m_alpha << " Convolution: " << vpMeS.m_convlt << " Weight: " << vpMeS.m_weight << " Threshold: " << vpMeS.m_contrastThreshold);
450 }
451 
452 END_VISP_NAMESPACE
friend std::ostream & operator<<(std::ostream &s, const vpArray2D< Type > &A)
Definition: vpArray2D.h:611
static const vpColor red
Definition: vpColor.h:217
static const vpColor cyan
Definition: vpColor.h:226
static const vpColor blue
Definition: vpColor.h:223
static const vpColor purple
Definition: vpColor.h:228
static const vpColor yellow
Definition: vpColor.h:225
static const vpColor green
Definition: vpColor.h:220
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
static void displayPoint(const vpImage< unsigned char > &I, const vpImagePoint &ip, const vpColor &color, unsigned int thickness=1)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
void set_j(double jj)
Definition: vpImagePoint.h:309
void set_i(double ii)
Definition: vpImagePoint.h:298
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getHeight() const
Definition: vpImage.h:181
static double sqr(double x)
Definition: vpMath.h:203
static int round(double x)
Definition: vpMath.h:409
Performs search in a given direction(normal) for a given distance(pixels) for a given 'site'....
Definition: vpMeSite.h:68
int m_mask_sign
Mask sign.
Definition: vpMeSite.h:107
vpMeSiteState
Definition: vpMeSite.h:85
@ OUTSIDE_ROI_MASK
Point is outside the region of interest mask, but retained in the ME list.
Definition: vpMeSite.h:95
@ THRESHOLD
Point not tracked due to the likelihood that is below the threshold, but retained in the ME list.
Definition: vpMeSite.h:91
@ CONTRAST
Point not tracked due to a contrast problem, but retained in the ME list.
Definition: vpMeSite.h:87
@ M_ESTIMATOR
Point detected as an outlier during virtual visual-servoing.
Definition: vpMeSite.h:92
@ NO_SUPPRESSION
Point successfully tracked.
Definition: vpMeSite.h:86
void setDisplay(vpMeSiteDisplayType select)
Definition: vpMeSite.h:250
int operator!=(const vpMeSite &m)
Definition: vpMeSite.cpp:381
void display(const vpImage< unsigned char > &I)
Definition: vpMeSite.cpp:383
double m_ifloat
Subpixel coordinates along i of a site.
Definition: vpMeSite.h:103
vpMeSite * getQueryList(const vpImage< unsigned char > &I, const int &range) const
Definition: vpMeSite.cpp:175
double m_normGradient
Convolution of Site in previous image.
Definition: vpMeSite.h:113
double convolution(const vpImage< unsigned char > &ima, const vpMe *me)
Definition: vpMeSite.cpp:214
double getContrastThreshold() const
Definition: vpMeSite.h:307
void init()
Definition: vpMeSite.cpp:57
double m_convlt
Convolution of Site in previous image.
Definition: vpMeSite.h:111
double m_alpha
Angle of tangent at site.
Definition: vpMeSite.h:109
@ NONE
Not displayed.
Definition: vpMeSite.h:75
@ RANGE_RESULT
Definition: vpMeSite.h:78
@ RESULT
Definition: vpMeSite.h:77
@ RANGE
Definition: vpMeSite.h:76
vpMeSite & operator=(const vpMeSite &m)
Definition: vpMeSite.cpp:157
double m_contrastThreshold
Old likelihood ratio threshold (to be avoided) or easy-to-use normalized threshold: minimal contrast.
Definition: vpMeSite.h:117
int m_j
Integer coordinates along j of a site.
Definition: vpMeSite.h:101
int m_i
Integer coordinate along i of a site.
Definition: vpMeSite.h:99
double m_jfloat
Subpixel coordinates along j of a site.
Definition: vpMeSite.h:105
vpMeSite()
Definition: vpMeSite.cpp:80
double m_weight
Uncertainty of point given as a probability between 0 and 1.
Definition: vpMeSite.h:115
void track(const vpImage< unsigned char > &I, const vpMe *me, const bool &test_contrast=true)
Definition: vpMeSite.cpp:269
Definition: vpMe.h:134
vpLikelihoodThresholdType getLikelihoodThresholdType() const
Definition: vpMe.h:327
unsigned int getAngleStep() const
Definition: vpMe.h:193
double getMu1() const
Definition: vpMe.h:240
int getStrip() const
Definition: vpMe.h:282
double getMu2() const
Definition: vpMe.h:247
unsigned int getMaskSize() const
Definition: vpMe.h:225
vpMatrix * getMask() const
Definition: vpMe.h:200
unsigned int getRange() const
Definition: vpMe.h:268
@ NORMALIZED_THRESHOLD
Definition: vpMe.h:145