Visual Servoing Platform  version 3.6.1 under development (2024-03-29)
vpImageMorphology.h
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 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 
34 #ifndef _vpImageMorphology_h_
35 #define _vpImageMorphology_h_
36 
42 #include <visp3/core/vpImage.h>
43 #include <visp3/core/vpImageException.h>
44 #include <visp3/core/vpMatrix.h>
45 
46 #include <fstream>
47 #include <iostream>
48 #include <math.h>
49 #include <string.h>
50 
62 class VISP_EXPORT vpImageMorphology
63 {
64 public:
68  typedef enum
69  {
70  CONNEXITY_4,
72  CONNEXITY_8
75  } vpConnexityType;
76 
77 private:
89  template <typename T>
90  static void imageOperation(vpImage<T> &I, const T &null_value, const T &(*operation)(const T &, const T &), const vpConnexityType &connexity = CONNEXITY_4);
91 
101  template <typename T>
102  static void imageOperation(vpImage<T> &I, const T &(*operation)(const T &, const T &), const int &size = 3);
103 
104 public:
105  template <class Type>
106  static void erosion(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity = CONNEXITY_4);
107 
108  template <class Type>
109  static void dilatation(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity = CONNEXITY_4);
110 
111  template <typename T>
112  static void erosion(vpImage<T> &I, const vpConnexityType &connexity = CONNEXITY_4);
113 
114  template <typename T>
115  static void dilatation(vpImage<T> &I, const vpConnexityType &connexity = CONNEXITY_4);
116 
117  template <typename T>
118  static void erosion(vpImage<T> &I, const int &size);
119 
120  template <typename T>
121  static void dilatation(vpImage<T> &I, const int &size);
122 
123 #if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
139  vp_deprecated static void erosion(vpImage<unsigned char> &I, const vpConnexityType &connexity = CONNEXITY_4)
140  {
141  vpImageMorphology::erosion<unsigned char>(I, connexity);
142  }
143 
156  vp_deprecated static void dilatation(vpImage<unsigned char> &I, const vpConnexityType &connexity = CONNEXITY_4)
157  {
158  vpImageMorphology::dilatation<unsigned char>(I, connexity);
159  }
161 #endif
162 };
163 
181 template <class Type>
182 void vpImageMorphology::erosion(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity)
183 {
184  if (I.getSize() == 0) {
185  std::cerr << "Input image is empty!" << std::endl;
186  return;
187  }
188 
189  vpImage<Type> J(I.getHeight() + 2, I.getWidth() + 2);
190  // Copy I to J and add border
191  for (unsigned int i = 0; i < J.getHeight(); i++) {
192  if (i == 0 || i == J.getHeight() - 1) {
193  for (unsigned int j = 0; j < J.getWidth(); j++) {
194  J[i][j] = value;
195  }
196  }
197  else {
198  J[i][0] = value;
199  memcpy(J[i] + 1, I[i - 1], sizeof(unsigned char) * I.getWidth());
200  J[i][J.getWidth() - 1] = value;
201  }
202  }
203 
204  if (connexity == CONNEXITY_4) {
205  for (unsigned int i = 0; i < I.getHeight(); i++) {
206  for (unsigned int j = 0; j < I.getWidth(); j++) {
207  if (J[i + 1][j + 1] == value) {
208  // Consider 4 neighbors
209  if ((J[i][j + 1] == value_out) || // Top
210  (J[i + 2][j + 1] == value_out) || // Bottom
211  (J[i + 1][j] == value_out) || // Left
212  (J[i + 1][j + 2] == value_out)) { // Right
213  I[i][j] = value_out;
214  }
215  }
216  }
217  }
218  }
219  else {
220  for (unsigned int i = 0; i < I.getHeight(); i++) {
221  for (unsigned int j = 0; j < I.getWidth(); j++) {
222  if (J[i + 1][j + 1] == value) {
223  // Consider 8 neighbors
224  if ((J[i][j] == value_out) || (J[i][j + 1] == value_out) || (J[i][j + 2] == value_out) ||
225  (J[i + 1][j] == value_out) || (J[i + 1][j + 2] == value_out) || (J[i + 2][j] == value_out) ||
226  (J[i + 2][j + 1] == value_out) || (J[i + 2][j + 2] == value_out))
227  I[i][j] = value_out;
228  }
229  }
230  }
231  }
232 }
233 
251 template <class Type>
252 void vpImageMorphology::dilatation(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity)
253 {
254  if (I.getSize() == 0) {
255  std::cerr << "Input image is empty!" << std::endl;
256  return;
257  }
258 
259  vpImage<Type> J(I.getHeight() + 2, I.getWidth() + 2);
260  // Copy I to J and add border
261  for (unsigned int i = 0; i < J.getHeight(); i++) {
262  if (i == 0 || i == J.getHeight() - 1) {
263  for (unsigned int j = 0; j < J.getWidth(); j++) {
264  J[i][j] = value_out;
265  }
266  }
267  else {
268  J[i][0] = value_out;
269  memcpy(J[i] + 1, I[i - 1], sizeof(unsigned char) * I.getWidth());
270  J[i][J.getWidth() - 1] = value_out;
271  }
272  }
273 
274  if (connexity == CONNEXITY_4) {
275  for (unsigned int i = 0; i < I.getHeight(); i++) {
276  for (unsigned int j = 0; j < I.getWidth(); j++) {
277  if (J[i + 1][j + 1] == value_out) {
278  // Consider 4 neighbors
279  if ((J[i][j + 1] == value) || // Top
280  (J[i + 2][j + 1] == value) || // Bottom
281  (J[i + 1][j] == value) || // Left
282  (J[i + 1][j + 2] == value)) { // Right
283  I[i][j] = value;
284  }
285  }
286  }
287  }
288  }
289  else {
290  for (unsigned int i = 0; i < I.getHeight(); i++) {
291  for (unsigned int j = 0; j < I.getWidth(); j++) {
292  if (J[i + 1][j + 1] == value_out) {
293  // Consider 8 neighbors
294  if ((J[i][j] == value) || (J[i][j + 1] == value) || (J[i][j + 2] == value) || (J[i + 1][j] == value) ||
295  (J[i + 1][j + 2] == value) || (J[i + 2][j] == value) || (J[i + 2][j + 1] == value) ||
296  (J[i + 2][j + 2] == value)) {
297  I[i][j] = value;
298  }
299  }
300  }
301  }
302  }
303 }
304 
305 template<typename T>
306 void vpImageMorphology::imageOperation(vpImage<T> &I, const T &null_value, const T &(*operation)(const T &, const T &), const vpConnexityType &connexity)
307 {
308  const int width_in = I.getWidth();
309  const int height_in = I.getHeight();
310  const int width_dilat = width_in + 2;
311  const int height_dilat = height_in + 2;
312  vpImage<T> J(height_dilat, width_dilat, null_value);
313 
314  // Copy I to J and add border
315  J.insert(I, vpImagePoint(1, 1));
316 
317  if (connexity == vpImageMorphology::CONNEXITY_4) {
318  const int nbOffset = 5;
319  int offset_x[nbOffset] = { 0, -1, 0, 1, 0 };
320  int offset_y[nbOffset] = { -1, 0, 0, 0, 1 };
321 
322  for (int i = 0; i < height_in; i++) {
323  for (int j = 0; j < width_in; j++) {
324  T value = null_value;
325  for (int k = 0; k < nbOffset; k++) {
326  value = operation(value, J[i + 1 + offset_y[k]][j + 1 + offset_x[k]]);
327  }
328 
329  I[i][j] = value;
330  }
331  }
332  }
333  else {
334  const int nbOffset = 9;
335  int offset_x[nbOffset] = { -1, 0, 1,-1, 0, 1,-1, 0, 1 };
336  int offset_y[nbOffset] = { -1,-1,-1, 0, 0, 0, 1, 1, 1 };
337 
338  for (int i = 0; i < height_in; i++) {
339  for (int j = 0; j < width_in; j++) {
340  T value = null_value;
341  for (int k = 0; k < nbOffset; k++) {
342  value = operation(value, J[i + 1 + offset_y[k]][j + 1 + offset_x[k]]);
343  }
344 
345  I[i][j] = value;
346  }
347  }
348  }
349 }
350 
374 template <typename T>
376 {
377  const T &(*operation)(const T & a, const T & b) = std::min;
378  vpImageMorphology::imageOperation(I, std::numeric_limits<T>::max(), operation, connexity);
379 }
380 
404 template <typename T>
406 {
407  const T &(*operation)(const T & a, const T & b) = std::max;
408  vpImageMorphology::imageOperation(I, std::numeric_limits<T>::min(), operation, connexity);
409 }
410 
411 template<typename T>
412 void vpImageMorphology::imageOperation(vpImage<T> &I, const T &(*operation)(const T &, const T &), const int &size)
413 {
414  if (size % 2 != 1) {
415  throw(vpException(vpException::badValue, "Dilatation/erosion kernel must be odd."));
416  }
417 
418  const int width_in = I.getWidth();
419  const int height_in = I.getHeight();
420  int halfKernelSize = size / 2;
421  vpImage<T> J = I;
422 
423  for (int r = 0; r < height_in; r++) {
424  // Computing the rows we can explore without going outside the limits of the image
425  int r_iterator_start = -halfKernelSize, r_iterator_stop = halfKernelSize + 1;
426  if (r - halfKernelSize < 0) {
427  r_iterator_start = -r;
428  }
429  else if (r + halfKernelSize >= height_in) {
430  r_iterator_stop = height_in - r;
431  }
432  for (int c = 0; c < width_in; c++) {
433  T value = I[r][c];
434  // Computing the columns we can explore without going outside the limits of the image
435  int c_iterator_start = -halfKernelSize, c_iterator_stop = halfKernelSize + 1;
436  if (c - halfKernelSize < 0) {
437  c_iterator_start = -c;
438  }
439  else if (c + halfKernelSize >= width_in) {
440  c_iterator_stop = width_in - c;
441  }
442  for (int r_iterator = r_iterator_start; r_iterator < r_iterator_stop; r_iterator++) {
443  for (int c_iterator = c_iterator_start; c_iterator < c_iterator_stop; c_iterator++) {
444  value = operation(value, J[r + r_iterator][c + c_iterator]);
445  }
446  }
447  I[r][c] = value;
448  }
449  }
450 }
451 
478 template <typename T>
479 void vpImageMorphology::erosion(vpImage<T> &I, const int &size)
480 {
481  const T &(*operation)(const T & a, const T & b) = std::min;
482  vpImageMorphology::imageOperation(I, operation, size);
483 }
484 
510 template<typename T>
511 void vpImageMorphology::dilatation(vpImage<T> &I, const int &size)
512 {
513  const T &(*operation)(const T & a, const T & b) = std::max;
514  vpImageMorphology::imageOperation(I, operation, size);
515 }
516 #endif
517 
518 /*
519  * Local variables:
520  * c-basic-offset: 2
521  * End:
522  */
error that can be emitted by ViSP classes.
Definition: vpException.h:59
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:85
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 void erosion(vpImage< Type > &I, Type value, Type value_out, vpConnexityType connexity=CONNEXITY_4)
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...
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:69
unsigned int getWidth() const
Definition: vpImage.h:245
unsigned int getSize() const
Definition: vpImage.h:224
unsigned int getHeight() const
Definition: vpImage.h:184