Visual Servoing Platform  version 3.2.0 under development (2019-01-22)
testFloodFill.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Test flood fill algorithm.
33  *
34  * Authors:
35  * Souriya Trinh
36  *
37  *****************************************************************************/
38 
39 #include <iomanip>
40 
41 #include <visp3/core/vpImageTools.h>
42 #include <visp3/core/vpIoTools.h>
43 #include <visp3/imgproc/vpImgproc.h>
44 #include <visp3/io/vpImageIo.h>
45 #include <visp3/io/vpParseArgv.h>
46 
53 // List of allowed command line options
54 #define GETOPTARGS "cdi:o:h"
55 
56 void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user);
57 bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user);
58 
59 /*
60  Print the program options.
61 
62  \param name : Program name.
63  \param badparam : Bad parameter name.
64  \param ipath: Input image path.
65  \param opath : Output image path.
66  \param user : Username.
67  */
68 void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user)
69 {
70  fprintf(stdout, "\n\
71 Test flood fill algorithm.\n\
72 \n\
73 SYNOPSIS\n\
74  %s [-i <input image path>] [-o <output image path>]\n\
75  [-h]\n \
76 ", name);
77 
78  fprintf(stdout, "\n\
79 OPTIONS: Default\n\
80  -i <input image path> %s\n\
81  Set image input path.\n\
82  From this path read \"Klimt/Klimt.pgm\"\n\
83  image.\n\
84  Setting the VISP_INPUT_IMAGE_PATH environment\n\
85  variable produces the same behaviour than using\n\
86  this option.\n\
87 \n\
88  -o <output image path> %s\n\
89  Set image output path.\n\
90  From this directory, creates the \"%s\"\n\
91  subdirectory depending on the username, where \n\
92  output result images are written.\n\
93 \n\
94  -h\n\
95  Print the help.\n\n", ipath.c_str(), opath.c_str(), user.c_str());
96 
97  if (badparam)
98  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
99 }
100 
111 bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user)
112 {
113  const char *optarg_;
114  int c;
115  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
116 
117  switch (c) {
118  case 'i':
119  ipath = optarg_;
120  break;
121  case 'o':
122  opath = optarg_;
123  break;
124  case 'h':
125  usage(argv[0], NULL, ipath, opath, user);
126  return false;
127  break;
128 
129  case 'c':
130  case 'd':
131  break;
132 
133  default:
134  usage(argv[0], optarg_, ipath, opath, user);
135  return false;
136  break;
137  }
138  }
139 
140  if ((c == 1) || (c == -1)) {
141  // standalone param or error
142  usage(argv[0], NULL, ipath, opath, user);
143  std::cerr << "ERROR: " << std::endl;
144  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
145  return false;
146  }
147 
148  return true;
149 }
150 
151 void printImage(const vpImage<unsigned char> &I, const std::string &name)
152 {
153  std::cout << "\n" << name << ":" << std::endl;
154 
155  std::cout << " ";
156  for (unsigned int j = 0; j < I.getWidth(); j++) {
157  std::cout << std::setfill(' ') << std::setw(2) << j << " ";
158  }
159  std::cout << std::endl;
160 
161  std::cout << std::setfill(' ') << std::setw(3) << "+";
162  for (unsigned int j = 0; j < I.getWidth(); j++) {
163  std::cout << std::setw(3) << "---";
164  }
165  std::cout << std::endl;
166 
167  for (unsigned int i = 0; i < I.getHeight(); i++) {
168  std::cout << std::setfill(' ') << std::setw(2) << i << "|";
169 
170  for (unsigned int j = 0; j < I.getWidth(); j++) {
171  std::cout << std::setfill(' ') << std::setw(2) << static_cast<unsigned int>(I[i][j]) << " ";
172  }
173 
174  std::cout << std::endl;
175  }
176 }
177 
178 int main(int argc, const char **argv)
179 {
180  try {
181  std::string env_ipath;
182  std::string opt_ipath;
183  std::string opt_opath;
184  std::string ipath;
185  std::string opath;
186  std::string filename;
187  std::string username;
188 
189  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
190  // environment variable value
191  env_ipath = vpIoTools::getViSPImagesDataPath();
192 
193  // Set the default input path
194  if (!env_ipath.empty())
195  ipath = env_ipath;
196 
197 // Set the default output path
198 #if defined(_WIN32)
199  opt_opath = "C:/temp";
200 #else
201  opt_opath = "/tmp";
202 #endif
203 
204  // Get the user login name
205  vpIoTools::getUserName(username);
206 
207  // Read the command line options
208  if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) {
209  exit(EXIT_FAILURE);
210  }
211 
212  // Get the option values
213  if (!opt_ipath.empty())
214  ipath = opt_ipath;
215  if (!opt_opath.empty())
216  opath = opt_opath;
217 
218  // Append to the output path string, the login name of the user
219  opath = vpIoTools::createFilePath(opath, username);
220 
221  // Test if the output path exist. If no try to create it
222  if (vpIoTools::checkDirectory(opath) == false) {
223  try {
224  // Create the dirname
226  } catch (...) {
227  usage(argv[0], NULL, ipath, opt_opath, username);
228  std::cerr << std::endl << "ERROR:" << std::endl;
229  std::cerr << " Cannot create " << opath << std::endl;
230  std::cerr << " Check your -o " << opt_opath << " option " << std::endl;
231  exit(EXIT_FAILURE);
232  }
233  }
234 
235  // Compare ipath and env_ipath. If they differ, we take into account
236  // the input path comming from the command line option
237  if (!opt_ipath.empty() && !env_ipath.empty()) {
238  if (ipath != env_ipath) {
239  std::cout << std::endl << "WARNING: " << std::endl;
240  std::cout << " Since -i <visp image path=" << ipath << "> "
241  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
242  << " we skip the environment variable." << std::endl;
243  }
244  }
245 
246  // Test if an input path is set
247  if (opt_ipath.empty() && env_ipath.empty()) {
248  usage(argv[0], NULL, ipath, opt_opath, username);
249  std::cerr << std::endl << "ERROR:" << std::endl;
250  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
251  << " environment variable to specify the location of the " << std::endl
252  << " image path where test images are located." << std::endl
253  << std::endl;
254  exit(EXIT_FAILURE);
255  }
256 
257  //
258  // Here starts really the test
259  //
260 
261  unsigned char image_data[8 * 8] = {1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0,
262  1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1,
263  1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0};
264  vpImage<unsigned char> I_test_flood_fill_4_connexity(image_data, 8, 8, true);
265  vpImage<unsigned char> I_test_flood_fill_8_connexity = I_test_flood_fill_4_connexity;
266  printImage(I_test_flood_fill_4_connexity, "Test image data");
267 
268  unsigned char image_data_check_4_connexity[8 * 8] = {
269  1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
270  1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0};
271  vpImage<unsigned char> I_check_4_connexity(image_data_check_4_connexity, 8, 8, true);
272 
273  unsigned char image_data_check_8_connexity[8 * 8] = {
274  1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
275  1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0};
276  vpImage<unsigned char> I_check_8_connexity(image_data_check_8_connexity, 8, 8, true);
277 
278  // Test flood fill on test data 4-connexity
279  vp::floodFill(I_test_flood_fill_4_connexity, vpImagePoint(2, 2), 0, 1, vpImageMorphology::CONNEXITY_4);
280  printImage(I_test_flood_fill_4_connexity, "I_test_flood_fill_4_connexity");
281 
282  if (I_test_flood_fill_4_connexity != I_check_4_connexity) {
283  throw vpException(vpException::fatalError, "Problem with vp::floodFill() and 4-connexity!");
284  }
285  std::cout << "\n(I_test_flood_fill_4_connexity == I_check_4_connexity)? "
286  << (I_test_flood_fill_4_connexity == I_check_4_connexity) << std::endl;
287 
288  // Test flood fill on test data 8-connexity
289  vp::floodFill(I_test_flood_fill_8_connexity, vpImagePoint(2, 2), 0, 1, vpImageMorphology::CONNEXITY_8);
290  printImage(I_test_flood_fill_8_connexity, "I_test_flood_fill_8_connexity");
291 
292  if (I_test_flood_fill_8_connexity != I_check_8_connexity) {
293  throw vpException(vpException::fatalError, "Problem with vp::floodFill() and 8-connexity!");
294  }
295  std::cout << "\n(I_test_flood_fill_8_connexity == I_check_8_connexity)? "
296  << (I_test_flood_fill_8_connexity == I_check_8_connexity) << std::endl;
297 
298  // Read Klimt.ppm
299  filename = vpIoTools::createFilePath(ipath, "Klimt/Klimt.pgm");
300  vpImage<unsigned char> I_klimt;
301  vpImageIo::read(I_klimt, filename);
302  std::cout << "\nRead image: " << filename << " (" << I_klimt.getWidth() << "x" << I_klimt.getHeight() << ")"
303  << std::endl
304  << std::endl;
305  vpImageTools::binarise(I_klimt, (unsigned char)127, (unsigned char)255, (unsigned char)0, (unsigned char)255,
306  (unsigned char)255);
307 
308  int seed_x = 0;
309  int seed_y = 0;
310 
311  vpImage<unsigned char> I_klimt_flood_fill_4_connexity = I_klimt;
312  double t = vpTime::measureTimeMs();
313  vp::floodFill(I_klimt_flood_fill_4_connexity, vpImagePoint(seed_y, seed_x), 0, 255, vpImageMorphology::CONNEXITY_4);
314  t = vpTime::measureTimeMs() - t;
315  std::cout << "Flood fill on Klimt image (4-connexity): " << t << " ms" << std::endl;
316 
317  filename = vpIoTools::createFilePath(opath, "Klimt_flood_fill_4_connexity.pgm");
318  vpImageIo::write(I_klimt_flood_fill_4_connexity, filename);
319 
320  vpImage<unsigned char> I_klimt_flood_fill_8_connexity = I_klimt;
321  t = vpTime::measureTimeMs();
322  vp::floodFill(I_klimt_flood_fill_8_connexity, vpImagePoint(seed_y, seed_x), 0, 255, vpImageMorphology::CONNEXITY_8);
323  t = vpTime::measureTimeMs() - t;
324  std::cout << "Flood fill on Klimt image (8-connexity): " << t << " ms" << std::endl;
325  filename = vpIoTools::createFilePath(opath, "Klimt_flood_fill_8_connexity.pgm");
326  vpImageIo::write(I_klimt_flood_fill_8_connexity, filename);
327 
328 #if VISP_HAVE_OPENCV_VERSION >= 0x020408
329  cv::Mat matImg_klimt_4_connexity, matImg_klimt_8_connexity;
330  vpImageConvert::convert(I_klimt, matImg_klimt_4_connexity);
331  vpImageConvert::convert(I_klimt, matImg_klimt_8_connexity);
332 
333  // 4-connexity
334  t = vpTime::measureTimeMs();
335  cv::floodFill(matImg_klimt_4_connexity, cv::Point(seed_x, seed_y), cv::Scalar(255), 0, cv::Scalar(), cv::Scalar(),
336  4);
337  t = vpTime::measureTimeMs() - t;
338  std::cout << "OpenCV flood fill on Klimt image (4-connexity): " << t << " ms" << std::endl;
339 
340  vpImage<unsigned char> I_klimt_flood_fill_4_connexity_check;
341  vpImageConvert::convert(matImg_klimt_4_connexity, I_klimt_flood_fill_4_connexity_check);
342 
343  filename = vpIoTools::createFilePath(opath, "Klimt_flood_fill_4_connexity_opencv.pgm");
344  vpImageIo::write(I_klimt_flood_fill_4_connexity_check, filename);
345 
346  // 8-connexity
347  t = vpTime::measureTimeMs();
348  cv::floodFill(matImg_klimt_8_connexity, cv::Point(seed_x, seed_y), cv::Scalar(255), 0, cv::Scalar(), cv::Scalar(),
349  8);
350  t = vpTime::measureTimeMs() - t;
351  std::cout << "OpenCV flood fill on Klimt image (8-connexity): " << t << " ms" << std::endl;
352 
353  vpImage<unsigned char> I_klimt_flood_fill_8_connexity_check;
354  vpImageConvert::convert(matImg_klimt_8_connexity, I_klimt_flood_fill_8_connexity_check);
355 
356  filename = vpIoTools::createFilePath(opath, "Klimt_flood_fill_8_connexity_opencv.pgm");
357  vpImageIo::write(I_klimt_flood_fill_8_connexity_check, filename);
358 
359  // Check
360  std::cout << "\n(I_klimt_flood_fill_4_connexity == "
361  "I_klimt_flood_fill_4_connexity_check)? "
362  << (I_klimt_flood_fill_4_connexity == I_klimt_flood_fill_4_connexity_check) << std::endl;
363  std::cout << "(I_klimt_flood_fill_8_connexity == "
364  "I_klimt_flood_fill_8_connexity_check)? "
365  << (I_klimt_flood_fill_8_connexity == I_klimt_flood_fill_8_connexity_check) << std::endl;
366 
367  if (I_klimt_flood_fill_4_connexity != I_klimt_flood_fill_4_connexity_check) {
368  throw vpException(vpException::fatalError, "(I_klimt_flood_fill_4_connexity != "
369  "I_klimt_flood_fill_4_connexity_check)");
370  }
371  if (I_klimt_flood_fill_8_connexity != I_klimt_flood_fill_8_connexity_check) {
372  throw vpException(vpException::fatalError, "(I_klimt_flood_fill_8_connexity != "
373  "I_klimt_flood_fill_8_connexity_check)");
374  }
375 #endif
376 
377  std::cout << "\nTest flood fill is ok!" << std::endl;
378  return EXIT_SUCCESS;
379  } catch (const vpException &e) {
380  std::cerr << "Catch an exception: " << e.what() << std::endl;
381  return EXIT_FAILURE;
382  }
383 }
static void binarise(vpImage< Type > &I, Type threshold1, Type threshold2, Type value1, Type value2, Type value3, const bool useLUT=true)
Definition: vpImageTools.h:430
static bool checkDirectory(const char *dirname)
Definition: vpIoTools.cpp:467
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1316
unsigned int getWidth() const
Definition: vpImage.h:239
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
error that can be emited by ViSP classes.
Definition: vpException.h:71
VISP_EXPORT double measureTimeMs()
Definition: vpTime.cpp:88
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
static void write(const vpImage< unsigned char > &I, const std::string &filename)
Definition: vpImageIo.cpp:375
static void makeDirectory(const char *dirname)
Definition: vpIoTools.cpp:597
const char * what() const
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1541
static std::string getUserName()
Definition: vpIoTools.cpp:298
VISP_EXPORT void floodFill(vpImage< unsigned char > &I, const vpImagePoint &seedPoint, const unsigned char oldValue, const unsigned char newValue, const vpImageMorphology::vpConnexityType &connexity=vpImageMorphology::CONNEXITY_4)
Definition: vpFloodFill.cpp:85
static void read(vpImage< unsigned char > &I, const std::string &filename)
Definition: vpImageIo.cpp:207
unsigned int getHeight() const
Definition: vpImage.h:178
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:88