Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
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 
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  virtual T operator()(const T &, const T &) = 0;
147  };
148 
149  template <typename T>
150  class vpPixelOperationMax : public vpPixelOperation<T>
151  {
152  public:
153  vpPixelOperationMax() { }
154 
155  virtual T operator()(const T &a, const T &b) VP_OVERRIDE
156  {
157  return std::max<T>(a, b);
158  }
159  };
160 
161  template <typename T>
162  class vpPixelOperationMin : public vpPixelOperation<T>
163  {
164  public:
165  vpPixelOperationMin() { }
166 
167  T operator()(const T &a, const T &b) VP_OVERRIDE
168  {
169  return std::min<T>(a, b);
170  }
171  };
172 
184  template <typename T>
185  static void imageOperation(vpImage<T> &I, const T &null_value, vpPixelOperation<T> *operation, const vpConnexityType &connexity = CONNEXITY_4);
186 
196  template <typename T>
197  static void imageOperation(vpImage<T> &I, vpPixelOperation<T> *operation, const int &size = 3);
198 
199 };
200 
218 template <class Type>
219 void vpImageMorphology::erosion(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity)
220 {
221  if (I.getSize() == 0) {
222  std::cerr << "Input image is empty!" << std::endl;
223  return;
224  }
225 
226  vpImage<Type> J(I.getHeight() + 2, I.getWidth() + 2);
227  // Copy I to J and add border
228  unsigned int j_height = J.getHeight();
229  unsigned int j_width = J.getWidth();
230  for (unsigned int i = 0; i < j_height; ++i) {
231  if ((i == 0) || (i == (j_height - 1))) {
232  for (unsigned int j = 0; j < j_width; ++j) {
233  J[i][j] = value;
234  }
235  }
236  else {
237  J[i][0] = value;
238  memcpy(J[i] + 1, I[i - 1], sizeof(unsigned char) * I.getWidth());
239  J[i][J.getWidth() - 1] = value;
240  }
241  }
242 
243  if (connexity == CONNEXITY_4) {
244  unsigned int i_height = I.getHeight();
245  unsigned int i_width = I.getWidth();
246  for (unsigned int i = 0; i < i_height; ++i) {
247  for (unsigned int j = 0; j < i_width; ++j) {
248  if (J[i + 1][j + 1] == value) {
249  // Consider 4 neighbors
250  if ((J[i][j + 1] == value_out) || // Top
251  (J[i + 2][j + 1] == value_out) || // Bottom
252  (J[i + 1][j] == value_out) || // Left
253  (J[i + 1][j + 2] == value_out)) { // Right
254  I[i][j] = value_out;
255  }
256  }
257  }
258  }
259  }
260  else {
261  unsigned int i_height = I.getHeight();
262  unsigned int i_width = I.getWidth();
263  for (unsigned int i = 0; i < i_height; ++i) {
264  for (unsigned int j = 0; j < i_width; ++j) {
265  if (J[i + 1][j + 1] == value) {
266  // Consider 8 neighbors
267  bool cond4firstneighbors = (J[i][j] == value_out) || (J[i][j + 1] == value_out) ||
268  (J[i][j + 2] == value_out) || (J[i + 1][j] == value_out);
269  bool cond4secondneighbors = (J[i + 1][j + 2] == value_out) || (J[i + 2][j] == value_out) ||
270  (J[i + 2][j + 1] == value_out) || (J[i + 2][j + 2] == value_out);
271  if (cond4firstneighbors || cond4secondneighbors) {
272  I[i][j] = value_out;
273  }
274  }
275  }
276  }
277  }
278 }
279 
297 template <class Type>
298 void vpImageMorphology::dilatation(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity)
299 {
300  if (I.getSize() == 0) {
301  std::cerr << "Input image is empty!" << std::endl;
302  return;
303  }
304 
305  vpImage<Type> J(I.getHeight() + 2, I.getWidth() + 2);
306  // Copy I to J and add border
307  unsigned int j_height = J.getHeight();
308  unsigned int j_width = J.getWidth();
309  for (unsigned int i = 0; i < j_height; ++i) {
310  if ((i == 0) || (i == (j_height - 1))) {
311  for (unsigned int j = 0; j < j_width; ++j) {
312  J[i][j] = value_out;
313  }
314  }
315  else {
316  J[i][0] = value_out;
317  memcpy(J[i] + 1, I[i - 1], sizeof(unsigned char) * I.getWidth());
318  J[i][J.getWidth() - 1] = value_out;
319  }
320  }
321 
322  if (connexity == CONNEXITY_4) {
323  unsigned int i_height = I.getHeight();
324  unsigned int i_width = I.getWidth();
325  for (unsigned int i = 0; i < i_height; ++i) {
326  for (unsigned int j = 0; j < i_width; ++j) {
327  if (J[i + 1][j + 1] == value_out) {
328  // Consider 4 neighbors
329  if ((J[i][j + 1] == value) || // Top
330  (J[i + 2][j + 1] == value) || // Bottom
331  (J[i + 1][j] == value) || // Left
332  (J[i + 1][j + 2] == value)) { // Right
333  I[i][j] = value;
334  }
335  }
336  }
337  }
338  }
339  else {
340  unsigned int i_height = I.getHeight();
341  unsigned int i_width = I.getWidth();
342  for (unsigned int i = 0; i < i_height; ++i) {
343  for (unsigned int j = 0; j < i_width; ++j) {
344  if (J[i + 1][j + 1] == value_out) {
345  // Consider 8 neighbors
346  bool cond4firstneighbors = (J[i][j] == value) || (J[i][j + 1] == value) || (J[i][j + 2] == value) || (J[i + 1][j] == value);
347  bool cond4secondneighbors = (J[i + 1][j + 2] == value) || (J[i + 2][j] == value) || (J[i + 2][j + 1] == value) ||
348  (J[i + 2][j + 2] == value);
349  if (cond4firstneighbors || cond4secondneighbors) {
350  I[i][j] = value;
351  }
352  }
353  }
354  }
355  }
356 }
357 
358 template<typename T>
359 void vpImageMorphology::imageOperation(vpImage<T> &I, const T &null_value, vpPixelOperation<T> *operation, const vpConnexityType &connexity)
360 {
361  const int width_in = I.getWidth();
362  const int height_in = I.getHeight();
363  const int width_dilat = width_in + 2;
364  const int height_dilat = height_in + 2;
365  vpImage<T> J(height_dilat, width_dilat, null_value);
366 
367  // Copy I to J and add border
368  J.insert(I, vpImagePoint(1, 1));
369 
370  if (connexity == vpImageMorphology::CONNEXITY_4) {
371  const int nbOffset = 5;
372  int offset_x[nbOffset] = { 0, -1, 0, 1, 0 };
373  int offset_y[nbOffset] = { -1, 0, 0, 0, 1 };
374 
375  for (int i = 0; i < height_in; ++i) {
376  for (int j = 0; j < width_in; ++j) {
377  T value = null_value;
378  for (int k = 0; k < nbOffset; ++k) {
379  value = (*operation)(value, J[i + 1 + offset_y[k]][j + 1 + offset_x[k]]);
380  }
381 
382  I[i][j] = value;
383  }
384  }
385  }
386  else {
387  const int nbOffset = 9;
388  int offset_x[nbOffset] = { -1, 0, 1,-1, 0, 1,-1, 0, 1 };
389  int offset_y[nbOffset] = { -1,-1,-1, 0, 0, 0, 1, 1, 1 };
390 
391  for (int i = 0; i < height_in; ++i) {
392  for (int j = 0; j < width_in; ++j) {
393  T value = null_value;
394  for (int k = 0; k < nbOffset; ++k) {
395  value = (*operation)(value, J[i + 1 + offset_y[k]][j + 1 + offset_x[k]]);
396  }
397 
398  I[i][j] = value;
399  }
400  }
401  }
402 }
403 
427 template <typename T>
429 {
430  vpPixelOperationMin<T> operation;
431  vpImageMorphology::imageOperation(I, std::numeric_limits<T>::max(), &operation, connexity);
432 }
433 
457 template <typename T>
459 {
460  vpPixelOperationMax<T> operation;
461  vpImageMorphology::imageOperation(I, std::numeric_limits<T>::min(), &operation, connexity);
462 }
463 
464 template<typename T>
465 void vpImageMorphology::imageOperation(vpImage<T> &I, vpPixelOperation<T> *operation, const int &size)
466 {
467  if ((size % 2) != 1) {
468  throw(vpException(vpException::badValue, "Dilatation/erosion kernel must be odd."));
469  }
470 
471  const int width_in = I.getWidth();
472  const int height_in = I.getHeight();
473  int halfKernelSize = size / 2;
474  vpImage<T> J = I;
475 
476  for (int r = 0; r < height_in; ++r) {
477  // Computing the rows we can explore without going outside the limits of the image
478  int r_iterator_start = -halfKernelSize, r_iterator_stop = halfKernelSize + 1;
479  if ((r - halfKernelSize) < 0) {
480  r_iterator_start = -r;
481  }
482  else if ((r + halfKernelSize) >= height_in) {
483  r_iterator_stop = height_in - r;
484  }
485  for (int c = 0; c < width_in; ++c) {
486  T value = I[r][c];
487  // Computing the columns we can explore without going outside the limits of the image
488  int c_iterator_start = -halfKernelSize, c_iterator_stop = halfKernelSize + 1;
489  if ((c - halfKernelSize) < 0) {
490  c_iterator_start = -c;
491  }
492  else if ((c + halfKernelSize) >= width_in) {
493  c_iterator_stop = width_in - c;
494  }
495  for (int r_iterator = r_iterator_start; r_iterator < r_iterator_stop; ++r_iterator) {
496  for (int c_iterator = c_iterator_start; c_iterator < c_iterator_stop; ++c_iterator) {
497  value = (*operation)(value, J[r + r_iterator][c + c_iterator]);
498  }
499  }
500  I[r][c] = value;
501  }
502  }
503 }
504 
531 template <typename T>
532 void vpImageMorphology::erosion(vpImage<T> &I, const int &size)
533 {
534  vpPixelOperationMin<T> operation;
535  vpImageMorphology::imageOperation(I, &operation, size);
536 }
537 
563 template<typename T>
564 void vpImageMorphology::dilatation(vpImage<T> &I, const int &size)
565 {
566  vpPixelOperationMax<T> operation;
567  vpImageMorphology::imageOperation(I, &operation, size);
568 }
569 END_VISP_NAMESPACE
570 #endif
571 
572 /*
573  * Local variables:
574  * c-basic-offset: 2
575  * End:
576  */
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 VP_DEPRECATED void erosion(vpImage< unsigned char > &I, const vpConnexityType &connexity=CONNEXITY_4)
An erosion is performed with a flat structuring element . The erosion using such a structuring elemen...
static VP_DEPRECATED void dilatation(vpImage< unsigned char > &I, const vpConnexityType &connexity=CONNEXITY_4)
A dilatation is performed with a flat structuring element . The erosion using such a structuring elem...
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