Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
testContours.cpp
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 * Test contours extraction.
32 */
33
39
40#include <iomanip>
41
42#include <visp3/core/vpImageTools.h>
43#include <visp3/core/vpIoTools.h>
44#include <visp3/imgproc/vpImgproc.h>
45#include <visp3/io/vpImageIo.h>
46#include <visp3/io/vpParseArgv.h>
47
48// List of allowed command line options
49#define GETOPTARGS "cdi:o:h"
50
51#ifdef ENABLE_VISP_NAMESPACE
52using namespace VISP_NAMESPACE_NAME;
53#endif
54
55void usage(const char *name, const char *badparam, const std::string &ipath, const std::string &opath, const std::string &user);
56bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user);
57
58/*
59 Print the program options.
60
61 \param name : Program name.
62 \param badparam : Bad parameter name.
63 \param ipath : Input image path.
64 \param opath : Output image path.
65 \param user : Username.
66 */
67void usage(const char *name, const char *badparam, const std::string &ipath, const std::string &opath, const std::string &user)
68{
69 fprintf(stdout, "\n\
70Test contours extraction.\n\
71\n\
72SYNOPSIS\n\
73 %s [-i <input image path>] [-o <output image path>]\n\
74 [-h]\n \
75",
76name);
77
78 fprintf(stdout, "\n\
79OPTIONS: 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",
96 ipath.c_str(), opath.c_str(), user.c_str());
97
98 if (badparam)
99 fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
100}
101
113bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, std::string user)
114{
115 const char *optarg_;
116 int c;
117 while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
118
119 switch (c) {
120 case 'i':
121 ipath = optarg_;
122 break;
123 case 'o':
124 opath = optarg_;
125 break;
126 case 'h':
127 usage(argv[0], nullptr, ipath, opath, user);
128 return false;
129
130 case 'c':
131 case 'd':
132 break;
133
134 default:
135 usage(argv[0], optarg_, ipath, opath, user);
136 return false;
137 }
138 }
139
140 if ((c == 1) || (c == -1)) {
141 // standalone param or error
142 usage(argv[0], nullptr, 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
151void 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 for (unsigned int i = 0; i < I.getHeight(); i++) {
162 std::cout << std::setfill(' ') << std::setw(2) << i << " ";
163
164 for (unsigned int j = 0; j < I.getWidth(); j++) {
165 std::cout << std::setfill(' ') << std::setw(2) << static_cast<unsigned int>(I[i][j]) << " ";
166 }
167
168 std::cout << std::endl;
169 }
170}
171
172void displayContourInfo(const VISP_NAMESPACE_NAME::vpContour &contour, int level)
173{
174 std::cout << "\nContour:" << std::endl;
175 std::cout << "\tlevel: " << level << std::endl;
176 std::cout << "\tcontour type: " << (contour.m_contourType == VISP_NAMESPACE_NAME::CONTOUR_OUTER ? "outer contour" : "hole contour")
177 << std::endl;
178 std::cout << "\tnb children: " << contour.m_children.size() << std::endl;
179
180 for (std::vector<VISP_NAMESPACE_NAME::vpContour *>::const_iterator it = contour.m_children.begin(); it != contour.m_children.end();
181 ++it) {
182 displayContourInfo(**it, level + 1);
183 }
184}
185
186int main(int argc, const char **argv)
187{
188 try {
189 std::string env_ipath;
190 std::string opt_ipath;
191 std::string opt_opath;
192 std::string ipath;
193 std::string opath;
194 std::string filename;
195 std::string username;
196
197 // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
198 // environment variable value
200
201 // Set the default input path
202 if (!env_ipath.empty())
203 ipath = env_ipath;
204
205// Set the default output path
206#if defined(_WIN32)
207 opt_opath = "C:/temp";
208#else
209 opt_opath = "/tmp";
210#endif
211
212 // Get the user login name
213 vpIoTools::getUserName(username);
214
215 // Read the command line options
216 if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) {
217 exit(EXIT_FAILURE);
218 }
219
220 // Get the option values
221 if (!opt_ipath.empty())
222 ipath = opt_ipath;
223 if (!opt_opath.empty())
224 opath = opt_opath;
225
226 // Append to the output path string, the login name of the user
227 opath = vpIoTools::createFilePath(opath, username);
228
229 // Test if the output path exist. If no try to create it
230 if (vpIoTools::checkDirectory(opath) == false) {
231 try {
232 // Create the dirname
234 }
235 catch (...) {
236 usage(argv[0], nullptr, ipath, opt_opath, username);
237 std::cerr << std::endl << "ERROR:" << std::endl;
238 std::cerr << " Cannot create " << opath << std::endl;
239 std::cerr << " Check your -o " << opt_opath << " option " << std::endl;
240 exit(EXIT_FAILURE);
241 }
242 }
243
244 // Compare ipath and env_ipath. If they differ, we take into account
245 // the input path coming from the command line option
246 if (!opt_ipath.empty() && !env_ipath.empty()) {
247 if (ipath != env_ipath) {
248 std::cout << std::endl << "WARNING: " << std::endl;
249 std::cout << " Since -i <visp image path=" << ipath << "> "
250 << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
251 << " we skip the environment variable." << std::endl;
252 }
253 }
254
255 // Test if an input path is set
256 if (opt_ipath.empty() && env_ipath.empty()) {
257 usage(argv[0], nullptr, ipath, opt_opath, username);
258 std::cerr << std::endl << "ERROR:" << std::endl;
259 std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
260 << " environment variable to specify the location of the " << std::endl
261 << " image path where test images are located." << std::endl
262 << std::endl;
263 exit(EXIT_FAILURE);
264 }
265
266 //
267 // Here starts really the test
268 //
269
270 unsigned char image_data[14 * 10] = {
271 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
272 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
273 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
274 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
275
276 vpImage<unsigned char> I_test_data(image_data, 14, 10, true);
277 std::cout << "Test with image data:" << std::endl;
278 printImage(I_test_data, "I_test_data");
279
281 std::vector<std::vector<vpImagePoint> > contours;
282 double t = vpTime::measureTimeMs();
283 VISP_NAMESPACE_NAME::findContours(I_test_data, vp_contours, contours);
285
286 displayContourInfo(vp_contours, 0);
287 std::cout << "ViSP: nb contours=" << contours.size() << " ; t=" << t << " ms" << std::endl;
288
289 // Read Klimt.ppm
290 filename = vpIoTools::createFilePath(ipath, "Klimt/Klimt.pgm");
292 std::cout << "Read image: " << filename << std::endl;
293 vpImageIo::read(I, filename);
294 vpImageTools::binarise(I, (unsigned char)127, (unsigned char)255, (unsigned char)0, (unsigned char)1,
295 (unsigned char)1);
296
297 vpImage<unsigned char> I2(I.getHeight(), I.getWidth());
298 for (unsigned int cpt = 0; cpt < I2.getSize(); cpt++) {
299 I2.bitmap[cpt] = 255 * I.bitmap[cpt];
300 }
301 filename = vpIoTools::createFilePath(opath, "Klimt_contours_binarise.pgm");
302 vpImageIo::write(I2, filename);
303
305 VISP_NAMESPACE_NAME::findContours(I, vp_contours, contours);
307
308 displayContourInfo(vp_contours, 0);
309 std::cout << "\nTest with Klimt image:" << std::endl;
310 std::cout << "ViSP: nb contours=" << contours.size() << " ; t=" << t << " ms" << std::endl;
311
312 // Draw and save
313 vpImage<unsigned char> I_draw_contours(I2.getHeight(), I2.getWidth(), 0);
314 VISP_NAMESPACE_NAME::drawContours(I_draw_contours, contours);
315
316 filename = vpIoTools::createFilePath(opath, "Klimt_contours_extracted.pgm");
317 vpImageIo::write(I_draw_contours, filename);
318
319 vpImage<vpRGBa> I_draw_contours_color(I2.getHeight(), I2.getWidth(), vpRGBa(0, 0, 0));
320 VISP_NAMESPACE_NAME::drawContours(I_draw_contours_color, contours, vpColor::red);
321
322 filename = vpIoTools::createFilePath(opath, "Klimt_contours_extracted_color.ppm");
323 vpImageIo::write(I_draw_contours_color, filename);
324
325 // Test retrieve list
327 vpImage<unsigned char> I_draw_contours_list(I2.getHeight(), I2.getWidth(), 0);
328
329 vpImage<unsigned char> I_tmp_list(I.getHeight(), I.getWidth(), 0);
330 VISP_NAMESPACE_NAME::drawContours(I_tmp_list, contours);
331
332 contours.clear();
333 for (std::vector<VISP_NAMESPACE_NAME::vpContour *>::const_iterator it = vp_contours.m_children.begin();
334 it != vp_contours.m_children.end(); ++it) {
335 contours.push_back((*it)->m_points);
336 }
337
338 VISP_NAMESPACE_NAME::drawContours(I_draw_contours_list, contours);
339 std::cout << "(I_tmp_list == I_draw_contours_list)? " << (I_tmp_list == I_draw_contours_list) << std::endl;
340
341 filename = vpIoTools::createFilePath(opath, "Klimt_contours_extracted_list.pgm");
342 vpImageIo::write(I_draw_contours_list, filename);
343
344 // Test retrieve external
346 vpImage<unsigned char> I_draw_contours_external(I2.getHeight(), I2.getWidth(), 0);
347
348 vpImage<unsigned char> I_tmp_external(I.getHeight(), I.getWidth(), 0);
349 VISP_NAMESPACE_NAME::drawContours(I_tmp_external, contours);
350
351 contours.clear();
352 for (std::vector<VISP_NAMESPACE_NAME::vpContour *>::const_iterator it = vp_contours.m_children.begin();
353 it != vp_contours.m_children.end(); ++it) {
354 contours.push_back((*it)->m_points);
355 }
356
357 VISP_NAMESPACE_NAME::drawContours(I_draw_contours_external, contours);
358 std::cout << "(I_tmp_external == I_draw_contours_external)? " << (I_tmp_external == I_draw_contours_external)
359 << std::endl;
360
361 filename = vpIoTools::createFilePath(opath, "Klimt_contours_extracted_external.pgm");
362 vpImageIo::write(I_draw_contours_external, filename);
363
364 // Test fillHoles
365 vpImage<unsigned char> I_holes = I_draw_contours_external;
366 vpImageTools::binarise(I_holes, (unsigned char)127, (unsigned char)255, (unsigned char)0, (unsigned char)255,
367 (unsigned char)255);
368
372 std::cout << "\nFill Holes: " << t << " ms" << std::endl;
373
374 filename = vpIoTools::createFilePath(opath, "Klimt_contours_extracted_external_fill_holes.pgm");
375 vpImageIo::write(I_holes, filename);
376
377#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x030000) && defined(HAVE_OPENCV_IMGPROC)
378 cv::Mat matImg;
379 vpImageConvert::convert(I, matImg);
380
381 std::vector<std::vector<cv::Point> > contours_opencv;
382 double t_opencv = vpTime::measureTimeMs();
383 cv::findContours(matImg, contours_opencv, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);
384 t_opencv = vpTime::measureTimeMs() - t_opencv;
385 std::cout << "\nOpenCV: nb contours=" << contours_opencv.size() << " ; t_opencv=" << t_opencv << " ms" << std::endl;
386
387 vpImage<unsigned char> I_draw_contours_opencv(I.getHeight(), I.getWidth(), 0);
388 for (std::vector<std::vector<cv::Point> >::const_iterator it1 = contours_opencv.begin();
389 it1 != contours_opencv.end(); ++it1) {
390 for (std::vector<cv::Point>::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) {
391 I_draw_contours_opencv[it2->y][it2->x] = 255;
392 }
393 }
394
395 std::cout << "(I_draw_contours_opencv == I_drawContours)? " << (I_draw_contours_opencv == I_draw_contours)
396 << std::endl;
397
398 filename = vpIoTools::createFilePath(opath, "Klimt_contours_extracted_opencv.pgm");
399 vpImageIo::write(I_draw_contours_opencv, filename);
400
401 // Test retrieve list
402 vpImageConvert::convert(I, matImg);
403 contours_opencv.clear();
404 cv::findContours(matImg, contours_opencv, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
405
406 I_draw_contours_opencv = 0;
407 for (std::vector<std::vector<cv::Point> >::const_iterator it1 = contours_opencv.begin();
408 it1 != contours_opencv.end(); ++it1) {
409 for (std::vector<cv::Point>::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) {
410 I_draw_contours_opencv[it2->y][it2->x] = 255;
411 }
412 }
413
414 std::cout << "(I_draw_contours_opencv == I_draw_contours_list)? "
415 << (I_draw_contours_opencv == I_draw_contours_list) << std::endl;
416
417// Test retrieve external
418 vpImageConvert::convert(I, matImg);
419 contours_opencv.clear();
420 cv::findContours(matImg, contours_opencv, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
421
422 I_draw_contours_opencv = 0;
423 for (std::vector<std::vector<cv::Point> >::const_iterator it1 = contours_opencv.begin();
424 it1 != contours_opencv.end(); ++it1) {
425 for (std::vector<cv::Point>::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) {
426 I_draw_contours_opencv[it2->y][it2->x] = 255;
427 }
428 }
429
430 std::cout << "(I_draw_contours_opencv == I_draw_contours_external)? "
431 << (I_draw_contours_opencv == I_draw_contours_external) << std::endl;
432#endif
433
434 return EXIT_SUCCESS;
435 }
436 catch (const vpException &e) {
437 std::cerr << "Catch an exception: " << e.what() << std::endl;
438 return EXIT_FAILURE;
439 }
440}
static const vpColor red
Definition vpColor.h:198
error that can be emitted by ViSP classes.
Definition vpException.h:60
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
static void write(const vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
static void binarise(vpImage< Type > &I, Type threshold1, Type threshold2, Type value1, Type value2, Type value3, bool useLUT=true)
Definition of the vpImage class member functions.
Definition vpImage.h:131
static std::string getViSPImagesDataPath()
static bool checkDirectory(const std::string &dirname)
static std::string getUserName()
static std::string createFilePath(const std::string &parent, const std::string &child)
static void makeDirectory(const std::string &dirname)
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
VISP_EXPORT void findContours(const VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I_original, vpContour &contours, std::vector< std::vector< VISP_NAMESPACE_ADDRESSING vpImagePoint > > &contourPts, const vpContourRetrievalType &retrievalMode=CONTOUR_RETR_TREE)
VISP_EXPORT void drawContours(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I, const std::vector< std::vector< VISP_NAMESPACE_ADDRESSING vpImagePoint > > &contours, unsigned char grayValue=255)
VISP_EXPORT void fillHoles(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I)
VISP_EXPORT double measureTimeMs()
std::vector< vpContour * > m_children
Children contour.
Definition vpContours.h:212
vpContourType m_contourType
Contour type.
Definition vpContours.h:214