Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpMorph.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  * Additional image morphology functions.
32  */
33 
39 #include <visp3/core/vpImageTools.h>
40 #include <visp3/imgproc/vpImgproc.h>
41 
42 namespace VISP_NAMESPACE_NAME
43 {
44 
46 #if USE_OLD_FILL_HOLE
47  ,
48  const vpImageMorphology::vpConnexityType &connexity
49 #endif
50 )
51 {
52  if (I.getSize() == 0) {
53  return;
54  }
55 
56 #if USE_OLD_FILL_HOLE
57  // Code similar to Matlab imfill(BW,'holes')
58  // Replaced by flood fill as imfill use imreconstruct
59  // and our reconstruct implementation is naive and inefficient
60  // Difference between new and old implementation:
61  // - new implementation allows to set the fill value
62  // - only background==0 is required, before it was 0 (background) / 1
63  // (foreground)
64  // - no more connexity option
65  vpImage<unsigned char> mask(I.getHeight() + 2, I.getWidth() + 2, 255);
66  // Copy I to mask + add border padding + complement
67  for (unsigned int i = 0; i < I.getHeight(); ++i) {
68  for (unsigned int j = 0; j < I.getWidth(); ++j) {
69  mask[i + 1][j + 1] = 255 - I[i][j];
70  }
71  }
72 
73  vpImage<unsigned char> marker(I.getHeight() + 2, I.getWidth() + 2, 0);
74  // Create marker with 255 1-pixel border
75  for (unsigned int i = 0; i < marker.getHeight(); ++i) {
76  if (i == 0 || i == marker.getHeight() - 1) {
77  for (unsigned int j = 0; j < marker.getWidth(); ++j) {
78  marker[i][j] = 255;
79  }
80  }
81  else {
82  marker[i][0] = 255;
83  marker[i][marker.getWidth() - 1] = 255;
84  }
85  }
86 
87  vpImage<unsigned char> I_reconstruct;
88  reconstruct(marker, mask, I_reconstruct, connexity);
89 
90  for (unsigned int i = 0; i < I.getHeight(); ++i) {
91  for (unsigned int j = 0; j < I.getWidth(); ++j) {
92  I[i][j] = 255 - I_reconstruct[i + 1][j + 1];
93  }
94  }
95 #else
96  // Create flood fill mask
97  vpImage<unsigned char> flood_fill_mask(I.getHeight() + 2, I.getWidth() + 2, 0);
98  // Copy I to mask + add border padding
99  unsigned int i_height = I.getHeight();
100  for (unsigned int i = 0; i < i_height; ++i) {
101  memcpy(flood_fill_mask[i + 1] + 1, I[i], sizeof(unsigned char) * I.getWidth());
102  }
103 
104  // Perform flood fill
105  const unsigned char newVal = 255;
106  floodFill(flood_fill_mask, vpImagePoint(0, 0), 0, newVal);
107 
108  // Get current mask
110  unsigned int mask_height = mask.getHeight();
111  for (unsigned int i = 0; i < mask_height; ++i) {
112  memcpy(mask[i], flood_fill_mask[i + 1] + 1, sizeof(unsigned char) * mask.getWidth());
113  }
114 
115  // Get image with holes filled
116  vpImage<unsigned char> I_white(I.getHeight(), I.getWidth(), 255), I_holes;
117  vpImageTools::imageSubtract(I_white, mask, I_holes);
118  vpImageTools::imageAdd(I, I_holes, I, true);
119 #endif
120 }
121 
123  vpImage<unsigned char> &h_kp1 /*alias I */, const vpImageMorphology::vpConnexityType &connexity)
124 {
125  if ((marker.getHeight() != mask.getHeight()) || (marker.getWidth() != mask.getWidth())) {
126  std::cerr << "marker.getHeight() != mask.getHeight() || "
127  "marker.getWidth() != mask.getWidth()"
128  << std::endl;
129  return;
130  }
131 
132  if (marker.getSize() == 0) {
133  std::cerr << "Input images are empty!" << std::endl;
134  return;
135  }
136 
137  vpImage<unsigned char> h_k = marker;
138  h_kp1 = h_k;
139 
140  bool h_kp1_eq_h_k = false;
141  do {
142  // Dilatation
143  vpImageMorphology::dilatation<unsigned char>(h_kp1, connexity);
144 
145  // Keep min
146  unsigned int h_kp1_height = h_kp1.getHeight();
147  unsigned int h_kp1_width = h_kp1.getWidth();
148  for (unsigned int i = 0; i < h_kp1_height; ++i) {
149  for (unsigned int j = 0; j < h_kp1_width; ++j) {
150  h_kp1[i][j] = std::min<unsigned char>(h_kp1[i][j], mask[i][j]);
151  }
152  }
153 
154  if (h_kp1 == h_k) {
155  h_kp1_eq_h_k = true;
156  // break
157  }
158  else {
159  h_k = h_kp1;
160  }
161  } while (h_kp1_eq_h_k == false);
162 }
163 
164 } // namespace
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
static void imageSubtract(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Ires, bool saturate=false)
static void imageAdd(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Ires, bool saturate=false)
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getSize() const
Definition: vpImage.h:221
unsigned int getHeight() const
Definition: vpImage.h:181
VISP_EXPORT void floodFill(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I, const VISP_NAMESPACE_ADDRESSING vpImagePoint &seedPoint, const unsigned char oldValue, const unsigned char newValue, const VISP_NAMESPACE_ADDRESSING vpImageMorphology::vpConnexityType &connexity=VISP_NAMESPACE_ADDRESSING vpImageMorphology::CONNEXITY_4)
VISP_EXPORT void fillHoles(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I)
VISP_EXPORT void reconstruct(const VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &marker, const VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &mask, VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &h_kp1, const VISP_NAMESPACE_ADDRESSING vpImageMorphology::vpConnexityType &connexity=VISP_NAMESPACE_ADDRESSING vpImageMorphology::CONNEXITY_4)