Visual Servoing Platform  version 3.6.1 under development (2025-02-18)
tutorial-flood-fill.cpp
1 
3 #include <cstdlib>
4 #include <iostream>
5 #include <visp3/core/vpConfig.h>
6 #include <visp3/core/vpImage.h>
7 #include <visp3/gui/vpDisplayFactory.h>
8 
9 #if defined(VISP_HAVE_MODULE_IMGPROC) && defined(VISP_HAVE_DISPLAY)
11 #include <visp3/imgproc/vpImgproc.h>
13 
14 #ifdef ENABLE_VISP_NAMESPACE
15 using namespace VISP_NAMESPACE_NAME;
16 #endif
17 
18 namespace
19 {
21 vpImagePoint switchToOctantZeroFrom(const int octant, const vpImagePoint &imPt)
22 {
23  vpImagePoint imPt_switched = imPt;
24 
25  switch (octant) {
26  case 0: // return (x, y)
27  imPt_switched.set_uv(imPt.get_u(), imPt.get_v());
28  break;
29 
30  case 1: // return (y, x)
31  imPt_switched.set_uv(imPt.get_v(), imPt.get_u());
32  break;
33 
34  case 2: // return (y, -x)
35  imPt_switched.set_uv(imPt.get_v(), -imPt.get_u());
36  break;
37 
38  case 3: // return (-x, y)
39  imPt_switched.set_uv(-imPt.get_u(), imPt.get_v());
40  break;
41 
42  case 4: // return (-x, -y)
43  imPt_switched.set_uv(-imPt.get_u(), -imPt.get_v());
44  break;
45 
46  case 5: // return (-y, -x)
47  imPt_switched.set_uv(-imPt.get_v(), -imPt.get_u());
48  break;
49 
50  case 6: // return (-y, x)
51  imPt_switched.set_uv(-imPt.get_v(), imPt.get_u());
52  break;
53 
54  case 7: // return (x, -y)
55  imPt_switched.set_uv(imPt.get_u(), -imPt.get_v());
56  break;
57 
58  default:
59  break;
60  }
61 
62  return imPt_switched;
63 }
64 
65 vpImagePoint switchFromOctantZeroTo(const int octant, const vpImagePoint &imPt)
66 {
67  vpImagePoint imPt_switched = imPt;
68 
69  switch (octant) {
70  case 0: // return (x, y)
71  imPt_switched.set_uv(imPt.get_u(), imPt.get_v());
72  break;
73 
74  case 1: // return (y, x)
75  imPt_switched.set_uv(imPt.get_v(), imPt.get_u());
76  break;
77 
78  case 2: // return (-y, x)
79  imPt_switched.set_uv(-imPt.get_v(), imPt.get_u());
80  break;
81 
82  case 3: // return (-x, y)
83  imPt_switched.set_uv(-imPt.get_u(), imPt.get_v());
84  break;
85 
86  case 4: // return (-x, -y)
87  imPt_switched.set_uv(-imPt.get_u(), -imPt.get_v());
88  break;
89 
90  case 5: // return (-y, -x)
91  imPt_switched.set_uv(-imPt.get_v(), -imPt.get_u());
92  break;
93 
94  case 6: // return (y, -x)
95  imPt_switched.set_uv(imPt.get_v(), -imPt.get_u());
96  break;
97 
98  case 7: // return (x, -y)
99  imPt_switched.set_uv(imPt.get_u(), -imPt.get_v());
100  break;
101 
102  default:
103  break;
104  }
105 
106  return imPt_switched;
107 }
108 
109 int getOctant(const vpImagePoint &imPt1, const vpImagePoint &imPt2)
110 {
111  double dx = imPt2.get_u() - imPt1.get_u();
112  double dy = imPt2.get_v() - imPt1.get_v();
113 
114  if (dx >= 0 && dy >= 0) {
115  if (dy >= dx) {
116  return 1;
117  }
118  else {
119  return 0;
120  }
121  }
122  else if (dx < 0 && dy >= 0) {
123  if (-dx >= dy) {
124  return 3;
125  }
126  else {
127  return 2;
128  }
129  }
130  else if (dx < 0 && dy < 0) {
131  if (dy <= dx) {
132  return 5;
133  }
134  else {
135  return 4;
136  }
137  }
138  else {
139  if (dx >= -dy) {
140  return 7;
141  }
142  else {
143  return 6;
144  }
145  }
146 }
147 
148 void drawLine(vpImage<unsigned char> &I, const unsigned char value, const vpImagePoint &imPt1_,
149  const vpImagePoint &imPt2_)
150 {
151  vpImagePoint imPt1((int)imPt1_.get_v(), (int)imPt1_.get_u());
152  vpImagePoint imPt2((int)imPt2_.get_v(), (int)imPt2_.get_u());
153 
154  int octant = getOctant(imPt1, imPt2);
155  imPt1 = switchToOctantZeroFrom(octant, imPt1);
156  imPt2 = switchToOctantZeroFrom(octant, imPt2);
157 
158  double dx = imPt2.get_u() - imPt1.get_u();
159  double dy = imPt2.get_v() - imPt1.get_v();
160  double D = 2 * dy - dx;
161  double y = imPt1.get_v();
162 
163  for (int x = (int)imPt1.get_u(); x <= (int)imPt2.get_u(); x++) {
164  vpImagePoint currentPt(y, x);
165  currentPt = switchFromOctantZeroTo(octant, currentPt);
166 
167  unsigned int i = std::min<unsigned int>(I.getHeight() - 1, (unsigned int)std::max<double>(0.0, currentPt.get_i()));
168  unsigned int j = std::min<unsigned int>(I.getWidth() - 1, (unsigned int)std::max<double>(0.0, currentPt.get_j()));
169  I[i][j] = value;
170 
171  if (D >= 0) {
172  y++;
173  D -= dx;
174  }
175 
176  D += dy;
177  }
178 }
180 } // namespace
181 
182 #endif
183 
184 int main()
185 {
187 #if defined(VISP_HAVE_MODULE_IMGPROC) && defined(VISP_HAVE_DISPLAY)
189 
191  vpImage<vpRGBa> I(480, 640, vpRGBa());
193 
194 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
195  std::shared_ptr<vpDisplay> display = vpDisplayFactory::createDisplay();
196 #else
198 #endif
199  display->init(I, 0, 0, "Paint");
200 
202  std::vector<vpPolygon> polygons;
203  for (int i = 0; i < 3; i++) {
205  std::stringstream ss;
206  ss << "Left click to draw polygon " << i + 1 << "/3"
207  << ", right click to close the shape.";
208  vpDisplay::displayText(I, 20, 20, ss.str(), vpColor::red);
209  vpDisplay::flush(I);
210 
211  vpPolygon polygon;
212  polygon.initClick(I);
213  polygons.push_back(polygon);
214 
216  vpDisplay::displayLine(I, polygon.getCorners(), true, vpColor::red);
217  vpDisplay::flush(I);
218 
219  // Update the lines draw internally in the current image
220  vpDisplay::getImage(I, I);
221  }
223 
225  vpImage<unsigned char> mask(I.getHeight(), I.getWidth(), 0);
226  for (size_t i = 0; i < polygons.size(); i++) {
227  if (polygons[i].getCorners().size() <= 1)
228  continue;
229 
230  for (size_t j = 0; j < polygons[i].getCorners().size() - 1; j++)
231  drawLine(mask, 255, polygons[i].getCorners()[j], polygons[i].getCorners()[j + 1]);
232 
233  drawLine(mask, 255, polygons[i].getCorners().front(), polygons[i].getCorners().back());
234  }
236 
237  bool quit = false;
238  while (!quit) {
240  vpDisplay::displayText(I, 20, 20,
241  "Left click on a pixel location to fill the "
242  "shape, right click to quit.",
243  vpColor::red);
244  vpDisplay::flush(I);
245 
247  vpImagePoint ip;
249  if (vpDisplay::getClick(I, ip, button, false))
251  {
252  switch (button) {
257 
259  for (unsigned int cpt = 0; cpt < mask.getSize(); cpt++) {
260  if (mask.bitmap[cpt])
261  I.bitmap[cpt] = vpColor::red;
262  }
264  break;
265 
267  quit = true;
268  break;
269 
270  default:
271  break;
272  }
273  }
274  }
275 
276 #if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11) && defined(VISP_HAVE_DISPLAY)
277  if (display != nullptr) {
278  delete display;
279  }
280 #endif
281 #endif
282 
283  return EXIT_SUCCESS;
284 }
static const vpColor red
Definition: vpColor.h:198
Class that defines generic functionalities for display.
Definition: vpDisplay.h:178
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)
static void getImage(const vpImage< unsigned char > &Is, vpImage< vpRGBa > &Id)
Definition: vpDisplay.cpp:140
static void flush(const vpImage< unsigned char > &I)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
double get_u() const
Definition: vpImagePoint.h:136
void set_uv(double u, double v)
Definition: vpImagePoint.h:357
double get_v() const
Definition: vpImagePoint.h:147
unsigned int getWidth() const
Definition: vpImage.h:242
Type * bitmap
points toward the bitmap
Definition: vpImage.h:135
unsigned int getHeight() const
Definition: vpImage.h:181
Defines a generic 2D polygon.
Definition: vpPolygon.h:103
const std::vector< vpImagePoint > & getCorners() const
Definition: vpPolygon.h:140
void initClick(const vpImage< unsigned char > &I, unsigned int size=5, const vpColor &color=vpColor::red, unsigned int thickness=1)
Definition: vpPolygon.cpp:267
Definition: vpRGBa.h:70
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)
std::shared_ptr< vpDisplay > createDisplay()
Return a smart pointer vpDisplay specialization if a GUI library is available or nullptr otherwise.
vpDisplay * allocateDisplay()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.