Visual Servoing Platform  version 3.6.1 under development (2025-02-19)
vpImageMorphology.h
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  * Morphology tools.
32  */
33 
40 #ifndef VP_IMAGE_MORPHOLOGY_H
41 #define VP_IMAGE_MORPHOLOGY_H
42 
43 #include <visp3/core/vpConfig.h>
44 #include <visp3/core/vpImage.h>
45 #include <visp3/core/vpImageException.h>
46 #include <visp3/core/vpMatrix.h>
47 
48 #include <fstream>
49 #include <iostream>
50 #include <math.h>
51 #include <string.h>
52 
53 BEGIN_VISP_NAMESPACE
65 class VISP_EXPORT vpImageMorphology
66 {
67 public:
71  typedef enum
72  {
73  CONNEXITY_4,
75  CONNEXITY_8
78  } vpConnexityType;
79 
80 public:
81  template <class Type>
82  static void erosion(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity = CONNEXITY_4);
83 
84  template <class Type>
85  static void dilatation(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity = CONNEXITY_4);
86 
87  template <typename T>
88  static void erosion(vpImage<T> &I, const vpConnexityType &connexity = CONNEXITY_4);
89 
90  template <typename T>
91  static void dilatation(vpImage<T> &I, const vpConnexityType &connexity = CONNEXITY_4);
92 
93  template <typename T>
94  static void erosion(vpImage<T> &I, const int &size);
95 
96  template <typename T>
97  static void dilatation(vpImage<T> &I, const int &size);
98 
99 #if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
115  VP_DEPRECATED static void erosion(vpImage<unsigned char> &I, const vpConnexityType &connexity = CONNEXITY_4)
116  {
117  vpImageMorphology::erosion<unsigned char>(I, connexity);
118  }
119 
132  VP_DEPRECATED static void dilatation(vpImage<unsigned char> &I, const vpConnexityType &connexity = CONNEXITY_4)
133  {
134  vpImageMorphology::dilatation<unsigned char>(I, connexity);
135  }
137 #endif
138 
139 private:
140  template <typename T>
141  class vpPixelOperation
142  {
143  public:
144  vpPixelOperation() { }
145 
146 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
147  virtual ~vpPixelOperation() = default;
148 #endif
149 
150  virtual T operator()(const T &, const T &) = 0;
151  };
152 
153  template <typename T>
154  class vpPixelOperationMax : public vpPixelOperation<T>
155  {
156  public:
157  vpPixelOperationMax() { }
158 
159  virtual T operator()(const T &a, const T &b) VP_OVERRIDE
160  {
161  return std::max<T>(a, b);
162  }
163  };
164 
165  template <typename T>
166  class vpPixelOperationMin : public vpPixelOperation<T>
167  {
168  public:
169  vpPixelOperationMin() { }
170 
171  T operator()(const T &a, const T &b) VP_OVERRIDE
172  {
173  return std::min<T>(a, b);
174  }
175  };
176 
188  template <typename T>
189  static void imageOperation(vpImage<T> &I, const T &null_value, vpPixelOperation<T> *operation, const vpConnexityType &connexity = CONNEXITY_4);
190 
200  template <typename T>
201  static void imageOperation(vpImage<T> &I, vpPixelOperation<T> *operation, const int &size = 3);
202 
203 };
204 
222 template <class Type>
223 void vpImageMorphology::erosion(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity)
224 {
225  if (I.getSize() == 0) {
226  std::cerr << "Input image is empty!" << std::endl;
227  return;
228  }
229 
230  vpImage<Type> J(I.getHeight() + 2, I.getWidth() + 2);
231  // Copy I to J and add border
232  unsigned int j_height = J.getHeight();
233  unsigned int j_width = J.getWidth();
234  for (unsigned int i = 0; i < j_height; ++i) {
235  if ((i == 0) || (i == (j_height - 1))) {
236  for (unsigned int j = 0; j < j_width; ++j) {
237  J[i][j] = value;
238  }
239  }
240  else {
241  J[i][0] = value;
242  memcpy(J[i] + 1, I[i - 1], sizeof(unsigned char) * I.getWidth());
243  J[i][J.getWidth() - 1] = value;
244  }
245  }
246 
247  if (connexity == CONNEXITY_4) {
248  unsigned int i_height = I.getHeight();
249  unsigned int i_width = I.getWidth();
250  for (unsigned int i = 0; i < i_height; ++i) {
251  for (unsigned int j = 0; j < i_width; ++j) {
252  if (J[i + 1][j + 1] == value) {
253  // Consider 4 neighbors
254  if ((J[i][j + 1] == value_out) || // Top
255  (J[i + 2][j + 1] == value_out) || // Bottom
256  (J[i + 1][j] == value_out) || // Left
257  (J[i + 1][j + 2] == value_out)) { // Right
258  I[i][j] = value_out;
259  }
260  }
261  }
262  }
263  }
264  else {
265  unsigned int i_height = I.getHeight();
266  unsigned int i_width = I.getWidth();
267  for (unsigned int i = 0; i < i_height; ++i) {
268  for (unsigned int j = 0; j < i_width; ++j) {
269  if (J[i + 1][j + 1] == value) {
270  // Consider 8 neighbors
271  bool cond4firstneighbors = (J[i][j] == value_out) || (J[i][j + 1] == value_out) ||
272  (J[i][j + 2] == value_out) || (J[i + 1][j] == value_out);
273  bool cond4secondneighbors = (J[i + 1][j + 2] == value_out) || (J[i + 2][j] == value_out) ||
274  (J[i + 2][j + 1] == value_out) || (J[i + 2][j + 2] == value_out);
275  if (cond4firstneighbors || cond4secondneighbors) {
276  I[i][j] = value_out;
277  }
278  }
279  }
280  }
281  }
282 }
283 
301 template <class Type>
302 void vpImageMorphology::dilatation(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity)
303 {
304  if (I.getSize() == 0) {
305  std::cerr << "Input image is empty!" << std::endl;
306  return;
307  }
308 
309  vpImage<Type> J(I.getHeight() + 2, I.getWidth() + 2);
310  // Copy I to J and add border
311  unsigned int j_height = J.getHeight();
312  unsigned int j_width = J.getWidth();
313  for (unsigned int i = 0; i < j_height; ++i) {
314  if ((i == 0) || (i == (j_height - 1))) {
315  for (unsigned int j = 0; j < j_width; ++j) {
316  J[i][j] = value_out;
317  }
318  }
319  else {
320  J[i][0] = value_out;
321  memcpy(J[i] + 1, I[i - 1], sizeof(unsigned char) * I.getWidth());
322  J[i][J.getWidth() - 1] = value_out;
323  }
324  }
325 
326  if (connexity == CONNEXITY_4) {
327  unsigned int i_height = I.getHeight();
328  unsigned int i_width = I.getWidth();
329  for (unsigned int i = 0; i < i_height; ++i) {
330  for (unsigned int j = 0; j < i_width; ++j) {
331  if (J[i + 1][j + 1] == value_out) {
332  // Consider 4 neighbors
333  if ((J[i][j + 1] == value) || // Top
334  (J[i + 2][j + 1] == value) || // Bottom
335  (J[i + 1][j] == value) || // Left
336  (J[i + 1][j + 2] == value)) { // Right
337  I[i][j] = value;
338  }
339  }
340  }
341  }
342  }
343  else {
344  unsigned int i_height = I.getHeight();
345  unsigned int i_width = I.getWidth();
346  for (unsigned int i = 0; i < i_height; ++i) {
347  for (unsigned int j = 0; j < i_width; ++j) {
348  if (J[i + 1][j + 1] == value_out) {
349  // Consider 8 neighbors
350  bool cond4firstneighbors = (J[i][j] == value) || (J[i][j + 1] == value) || (J[i][j + 2] == value) || (J[i + 1][j] == value);
351  bool cond4secondneighbors = (J[i + 1][j + 2] == value) || (J[i + 2][j] == value) || (J[i + 2][j + 1] == value) ||
352  (J[i + 2][j + 2] == value);
353  if (cond4firstneighbors || cond4secondneighbors) {
354  I[i][j] = value;
355  }
356  }
357  }
358  }
359  }
360 }
361 
362 template<typename T>
363 void vpImageMorphology::imageOperation(vpImage<T> &I, const T &null_value, vpPixelOperation<T> *operation, const vpConnexityType &connexity)
364 {
365  const int width_in = I.getWidth();
366  const int height_in = I.getHeight();
367  const int width_dilat = width_in + 2;
368  const int height_dilat = height_in + 2;
369  vpImage<T> J(height_dilat, width_dilat, null_value);
370 
371  // Copy I to J and add border
372  J.insert(I, vpImagePoint(1, 1));
373 
374  if (connexity == vpImageMorphology::CONNEXITY_4) {
375  const int nbOffset = 5;
376  int offset_x[nbOffset] = { 0, -1, 0, 1, 0 };
377  int offset_y[nbOffset] = { -1, 0, 0, 0, 1 };
378 
379  for (int i = 0; i < height_in; ++i) {
380  for (int j = 0; j < width_in; ++j) {
381  T value = null_value;
382  for (int k = 0; k < nbOffset; ++k) {
383  value = (*operation)(value, J[i + 1 + offset_y[k]][j + 1 + offset_x[k]]);
384  }
385 
386  I[i][j] = value;
387  }
388  }
389  }
390  else {
391  const int nbOffset = 9;
392  int offset_x[nbOffset] = { -1, 0, 1,-1, 0, 1,-1, 0, 1 };
393  int offset_y[nbOffset] = { -1,-1,-1, 0, 0, 0, 1, 1, 1 };
394 
395  for (int i = 0; i < height_in; ++i) {
396  for (int j = 0; j < width_in; ++j) {
397  T value = null_value;
398  for (int k = 0; k < nbOffset; ++k) {
399  value = (*operation)(value, J[i + 1 + offset_y[k]][j + 1 + offset_x[k]]);
400  }
401 
402  I[i][j] = value;
403  }
404  }
405  }
406 }
407 
431 template <typename T>
433 {
434  vpPixelOperationMin<T> operation;
435  vpImageMorphology::imageOperation(I, std::numeric_limits<T>::max(), &operation, connexity);
436 }
437 
461 template <typename T>
463 {
464  vpPixelOperationMax<T> operation;
465  vpImageMorphology::imageOperation(I, std::numeric_limits<T>::min(), &operation, connexity);
466 }
467 
468 template<typename T>
469 void vpImageMorphology::imageOperation(vpImage<T> &I, vpPixelOperation<T> *operation, const int &size)
470 {
471  if ((size % 2) != 1) {
472  throw(vpException(vpException::badValue, "Dilatation/erosion kernel must be odd."));
473  }
474 
475  const int width_in = I.getWidth();
476  const int height_in = I.getHeight();
477  int halfKernelSize = size / 2;
478  vpImage<T> J = I;
479 
480  for (int r = 0; r < height_in; ++r) {
481  // Computing the rows we can explore without going outside the limits of the image
482  int r_iterator_start = -halfKernelSize, r_iterator_stop = halfKernelSize + 1;
483  if ((r - halfKernelSize) < 0) {
484  r_iterator_start = -r;
485  }
486  else if ((r + halfKernelSize) >= height_in) {
487  r_iterator_stop = height_in - r;
488  }
489  for (int c = 0; c < width_in; ++c) {
490  T value = I[r][c];
491  // Computing the columns we can explore without going outside the limits of the image
492  int c_iterator_start = -halfKernelSize, c_iterator_stop = halfKernelSize + 1;
493  if ((c - halfKernelSize) < 0) {
494  c_iterator_start = -c;
495  }
496  else if ((c + halfKernelSize) >= width_in) {
497  c_iterator_stop = width_in - c;
498  }
499  for (int r_iterator = r_iterator_start; r_iterator < r_iterator_stop; ++r_iterator) {
500  for (int c_iterator = c_iterator_start; c_iterator < c_iterator_stop; ++c_iterator) {
501  value = (*operation)(value, J[r + r_iterator][c + c_iterator]);
502  }
503  }
504  I[r][c] = value;
505  }
506  }
507 }
508 
535 template <typename T>
536 void vpImageMorphology::erosion(vpImage<T> &I, const int &size)
537 {
538  vpPixelOperationMin<T> operation;
539  vpImageMorphology::imageOperation(I, &operation, size);
540 }
541 
567 template<typename T>
568 void vpImageMorphology::dilatation(vpImage<T> &I, const int &size)
569 {
570  vpPixelOperationMax<T> operation;
571  vpImageMorphology::imageOperation(I, &operation, size);
572 }
573 END_VISP_NAMESPACE
574 #endif
575 
576 /*
577  * Local variables:
578  * c-basic-offset: 2
579  * End:
580  */
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
Various mathematical morphology tools, erosion, dilatation...
static void dilatation(vpImage< Type > &I, Type value, Type value_out, vpConnexityType connexity=CONNEXITY_4)
static void erosion(vpImage< Type > &I, Type value, Type value_out, vpConnexityType connexity=CONNEXITY_4)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
Definition of the vpImage class member functions.
Definition: vpImage.h:131
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getSize() const
Definition: vpImage.h:221
unsigned int getHeight() const
Definition: vpImage.h:181