Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpImageMorphology.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 * Morphology tools.
32 */
33
39
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#if defined(__clang__)
54// Mute warning : '\tparam' command used in a comment that is not attached to a template declaration [-Wdocumentation]
55# pragma clang diagnostic push
56# pragma clang diagnostic ignored "-Wexit-time-destructors"
57#endif
58
67class VISP_EXPORT vpImageMorphology
68{
69public:
73 typedef enum
74 {
80 } vpConnexityType;
81
82public:
83 template <class Type>
84 static void erosion(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity = CONNEXITY_4);
85
86 template <class Type>
87 static void dilatation(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity = CONNEXITY_4);
88
89 template <typename T>
90 static void erosion(vpImage<T> &I, const vpConnexityType &connexity = CONNEXITY_4);
91
92 template <typename T>
93 static void dilatation(vpImage<T> &I, const vpConnexityType &connexity = CONNEXITY_4);
94
95 template <typename T>
96 static void erosion(vpImage<T> &I, const int &size);
97
98 template <typename T>
99 static void dilatation(vpImage<T> &I, const int &size);
100
101#if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
106
117 VP_DEPRECATED static void erosion(vpImage<unsigned char> &I, const vpConnexityType &connexity = CONNEXITY_4)
118 {
120 }
121
134 VP_DEPRECATED static void dilatation(vpImage<unsigned char> &I, const vpConnexityType &connexity = CONNEXITY_4)
135 {
137 }
138
139#endif
140
141private:
142 template <typename T>
143 class vpPixelOperation
144 {
145 public:
146 vpPixelOperation() { }
147
148#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
149 virtual ~vpPixelOperation() = default;
150#endif
151
152 virtual T operator()(const T &, const T &) = 0;
153 };
154
155 template <typename T>
156 class vpPixelOperationMax : public vpPixelOperation<T>
157 {
158 public:
159 vpPixelOperationMax() { }
160
161 virtual T operator()(const T &a, const T &b) VP_OVERRIDE
162 {
163 return std::max<T>(a, b);
164 }
165 };
166
167 template <typename T>
168 class vpPixelOperationMin : public vpPixelOperation<T>
169 {
170 public:
171 vpPixelOperationMin() { }
172
173 T operator()(const T &a, const T &b) VP_OVERRIDE
174 {
175 return std::min<T>(a, b);
176 }
177 };
178
190 template <typename T>
191 static void imageOperation(vpImage<T> &I, const T &null_value, vpPixelOperation<T> *operation, const vpConnexityType &connexity = CONNEXITY_4);
192
202 template <typename T>
203 static void imageOperation(vpImage<T> &I, vpPixelOperation<T> *operation, const int &size = 3);
204
205};
206
224template <class Type>
225void vpImageMorphology::erosion(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity)
226{
227 if (I.getSize() == 0) {
228 std::cerr << "Input image is empty!" << std::endl;
229 return;
230 }
231
232 vpImage<Type> J(I.getHeight() + 2, I.getWidth() + 2);
233 // Copy I to J and add border
234 unsigned int j_height = J.getHeight();
235 unsigned int j_width = J.getWidth();
236 for (unsigned int i = 0; i < j_height; ++i) {
237 if ((i == 0) || (i == (j_height - 1))) {
238 for (unsigned int j = 0; j < j_width; ++j) {
239 J[i][j] = value;
240 }
241 }
242 else {
243 J[i][0] = value;
244 memcpy(J[i] + 1, I[i - 1], sizeof(unsigned char) * I.getWidth());
245 J[i][J.getWidth() - 1] = value;
246 }
247 }
248
249 if (connexity == CONNEXITY_4) {
250 unsigned int i_height = I.getHeight();
251 unsigned int i_width = I.getWidth();
252 for (unsigned int i = 0; i < i_height; ++i) {
253 for (unsigned int j = 0; j < i_width; ++j) {
254 if (J[i + 1][j + 1] == value) {
255 // Consider 4 neighbors
256 if ((J[i][j + 1] == value_out) || // Top
257 (J[i + 2][j + 1] == value_out) || // Bottom
258 (J[i + 1][j] == value_out) || // Left
259 (J[i + 1][j + 2] == value_out)) { // Right
260 I[i][j] = value_out;
261 }
262 }
263 }
264 }
265 }
266 else {
267 unsigned int i_height = I.getHeight();
268 unsigned int i_width = I.getWidth();
269 for (unsigned int i = 0; i < i_height; ++i) {
270 for (unsigned int j = 0; j < i_width; ++j) {
271 if (J[i + 1][j + 1] == value) {
272 // Consider 8 neighbors
273 bool cond4firstneighbors = (J[i][j] == value_out) || (J[i][j + 1] == value_out) ||
274 (J[i][j + 2] == value_out) || (J[i + 1][j] == value_out);
275 bool cond4secondneighbors = (J[i + 1][j + 2] == value_out) || (J[i + 2][j] == value_out) ||
276 (J[i + 2][j + 1] == value_out) || (J[i + 2][j + 2] == value_out);
277 if (cond4firstneighbors || cond4secondneighbors) {
278 I[i][j] = value_out;
279 }
280 }
281 }
282 }
283 }
284}
285
303template <class Type>
304void vpImageMorphology::dilatation(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity)
305{
306 if (I.getSize() == 0) {
307 std::cerr << "Input image is empty!" << std::endl;
308 return;
309 }
310
311 vpImage<Type> J(I.getHeight() + 2, I.getWidth() + 2);
312 // Copy I to J and add border
313 unsigned int j_height = J.getHeight();
314 unsigned int j_width = J.getWidth();
315 for (unsigned int i = 0; i < j_height; ++i) {
316 if ((i == 0) || (i == (j_height - 1))) {
317 for (unsigned int j = 0; j < j_width; ++j) {
318 J[i][j] = value_out;
319 }
320 }
321 else {
322 J[i][0] = value_out;
323 memcpy(J[i] + 1, I[i - 1], sizeof(unsigned char) * I.getWidth());
324 J[i][J.getWidth() - 1] = value_out;
325 }
326 }
327
328 if (connexity == CONNEXITY_4) {
329 unsigned int i_height = I.getHeight();
330 unsigned int i_width = I.getWidth();
331 for (unsigned int i = 0; i < i_height; ++i) {
332 for (unsigned int j = 0; j < i_width; ++j) {
333 if (J[i + 1][j + 1] == value_out) {
334 // Consider 4 neighbors
335 if ((J[i][j + 1] == value) || // Top
336 (J[i + 2][j + 1] == value) || // Bottom
337 (J[i + 1][j] == value) || // Left
338 (J[i + 1][j + 2] == value)) { // Right
339 I[i][j] = value;
340 }
341 }
342 }
343 }
344 }
345 else {
346 unsigned int i_height = I.getHeight();
347 unsigned int i_width = I.getWidth();
348 for (unsigned int i = 0; i < i_height; ++i) {
349 for (unsigned int j = 0; j < i_width; ++j) {
350 if (J[i + 1][j + 1] == value_out) {
351 // Consider 8 neighbors
352 bool cond4firstneighbors = (J[i][j] == value) || (J[i][j + 1] == value) || (J[i][j + 2] == value) || (J[i + 1][j] == value);
353 bool cond4secondneighbors = (J[i + 1][j + 2] == value) || (J[i + 2][j] == value) || (J[i + 2][j + 1] == value) ||
354 (J[i + 2][j + 2] == value);
355 if (cond4firstneighbors || cond4secondneighbors) {
356 I[i][j] = value;
357 }
358 }
359 }
360 }
361 }
362}
363
364template<typename T>
365void vpImageMorphology::imageOperation(vpImage<T> &I, const T &null_value, vpPixelOperation<T> *operation, const vpConnexityType &connexity)
366{
367 const int width_in = static_cast<int>(I.getWidth());
368 const int height_in = static_cast<int>(I.getHeight());
369 const unsigned int width_dilat = I.getWidth() + 2;
370 const unsigned int height_dilat = I.getHeight() + 2;
371 vpImage<T> J(height_dilat, width_dilat, null_value);
372
373 // Copy I to J and add border
374 J.insert(I, vpImagePoint(1, 1));
375
376 if (connexity == vpImageMorphology::CONNEXITY_4) {
377 const int nbOffset = 5;
378 int offset_x[nbOffset] = { 0, -1, 0, 1, 0 };
379 int offset_y[nbOffset] = { -1, 0, 0, 0, 1 };
380
381 for (int i = 0; i < height_in; ++i) {
382 for (int j = 0; j < width_in; ++j) {
383 T value = null_value;
384 for (int k = 0; k < nbOffset; ++k) {
385 value = (*operation)(value, J[i + 1 + offset_y[k]][j + 1 + offset_x[k]]);
386 }
387
388 I[i][j] = value;
389 }
390 }
391 }
392 else {
393 const int nbOffset = 9;
394 int offset_x[nbOffset] = { -1, 0, 1,-1, 0, 1,-1, 0, 1 };
395 int offset_y[nbOffset] = { -1,-1,-1, 0, 0, 0, 1, 1, 1 };
396
397 for (int i = 0; i < height_in; ++i) {
398 for (int j = 0; j < width_in; ++j) {
399 T value = null_value;
400 for (int k = 0; k < nbOffset; ++k) {
401 value = (*operation)(value, J[i + 1 + offset_y[k]][j + 1 + offset_x[k]]);
402 }
403
404 I[i][j] = value;
405 }
406 }
407 }
408}
409
433template <typename T>
435{
436 vpPixelOperationMin<T> operation;
437 vpImageMorphology::imageOperation(I, std::numeric_limits<T>::max(), &operation, connexity);
438}
439
463template <typename T>
465{
466 vpPixelOperationMax<T> operation;
467 vpImageMorphology::imageOperation(I, std::numeric_limits<T>::min(), &operation, connexity);
468}
469
470template<typename T>
471void vpImageMorphology::imageOperation(vpImage<T> &I, vpPixelOperation<T> *operation, const int &size)
472{
473 if ((size % 2) != 1) {
474 throw(vpException(vpException::badValue, "Dilatation/erosion kernel must be odd."));
475 }
476
477 const int width_in = I.getWidth();
478 const int height_in = I.getHeight();
479 int halfKernelSize = size / 2;
480 vpImage<T> J = I;
481
482 for (int r = 0; r < height_in; ++r) {
483 // Computing the rows we can explore without going outside the limits of the image
484 int r_iterator_start = -halfKernelSize, r_iterator_stop = halfKernelSize + 1;
485 if ((r - halfKernelSize) < 0) {
486 r_iterator_start = -r;
487 }
488 else if ((r + halfKernelSize) >= height_in) {
489 r_iterator_stop = height_in - r;
490 }
491 for (int c = 0; c < width_in; ++c) {
492 T value = I[r][c];
493 // Computing the columns we can explore without going outside the limits of the image
494 int c_iterator_start = -halfKernelSize, c_iterator_stop = halfKernelSize + 1;
495 if ((c - halfKernelSize) < 0) {
496 c_iterator_start = -c;
497 }
498 else if ((c + halfKernelSize) >= width_in) {
499 c_iterator_stop = width_in - c;
500 }
501 for (int r_iterator = r_iterator_start; r_iterator < r_iterator_stop; ++r_iterator) {
502 for (int c_iterator = c_iterator_start; c_iterator < c_iterator_stop; ++c_iterator) {
503 value = (*operation)(value, J[r + r_iterator][c + c_iterator]);
504 }
505 }
506 I[r][c] = value;
507 }
508 }
509}
510
537template <typename T>
538void vpImageMorphology::erosion(vpImage<T> &I, const int &size)
539{
540 vpPixelOperationMin<T> operation;
541 vpImageMorphology::imageOperation(I, &operation, size);
542}
543
569template<typename T>
571{
572 vpPixelOperationMax<T> operation;
573 vpImageMorphology::imageOperation(I, &operation, size);
574}
575END_VISP_NAMESPACE
576
577#if defined(__clang__)
578# pragma clang diagnostic pop
579#endif
580
581#endif
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 of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getWidth() const
Definition vpImage.h:242
unsigned int getHeight() const
Definition vpImage.h:181