33#include <visp3/core/vpConfig.h>
35#include <visp3/core/vpCannyEdgeDetection.h>
36#include <visp3/core/vpImageFilter.h>
37#include <visp3/core/vpTime.h>
38#include <visp3/io/vpImageIo.h>
40#ifdef HAVE_OPENCV_IMGPROC
41#include <opencv2/imgproc/imgproc.hpp>
44#include "drawingHelpers.h"
46#ifdef ENABLE_VISP_NAMESPACE
80#ifdef VISP_HAVE_OPENCV
89void setGradientOutsideClass(
const vpImage<unsigned char> &I,
const int &gaussianKernelSize,
const float &gaussianStdev,
98void computeMeanMaxStdev(
const vpImage<T> &I,
float &mean,
float &max,
float &stdev)
100 max = std::numeric_limits<float>::epsilon();
103 unsigned int nbRows = I.getRows();
104 unsigned int nbCols = I.getCols();
105 float scale = 1.f / (
static_cast<float>(nbRows) *
static_cast<float>(nbCols));
106 for (
unsigned int r = 0;
r < nbRows;
r++) {
107 for (
unsigned int c = 0; c < nbCols; c++) {
109 max = std::max<float>(max,
static_cast<float>(I[r][c]));
113 for (
unsigned int r = 0;
r < nbRows;
r++) {
114 for (
unsigned int c = 0; c < nbCols; c++) {
115 stdev += (I[
r][c] - mean) * (I[r][c] - mean);
119 stdev = std::sqrt(stdev);
122void setGradientOutsideClass(
const vpImage<unsigned char> &I,
const int &gaussianKernelSize,
const float &gaussianStdev,
130 apertureSize, filteringType);
136 float mean, max, stdev;
137 computeMeanMaxStdev(dIx, mean, max, stdev);
139#if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
140 std::string title =
"Gradient along the horizontal axis. Mean = " + std::to_string(mean)
141 +
"+/-" + std::to_string(stdev) +
" Max = " + std::to_string(max);
145 std::stringstream ss;
146 ss <<
"Gradient along the horizontal axis. Mean = " << mean<<
"+/-" << stdev<<
" Max = " << max;
151 drawingHelpers::display(dIx_uchar, title);
152 computeMeanMaxStdev(dIy, mean, max, stdev);
153#if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
154 title =
"Gradient along the horizontal axis. Mean = " + std::to_string(mean)
155 +
"+/-" + std::to_string(stdev) +
" Max = " + std::to_string(max);
158 std::stringstream ss;
159 ss <<
"Gradient along the horizontal axis. Mean = " << mean<<
"+/-" << stdev<<
" Max = " << max;
164 drawingHelpers::display(dIy_uchar, title);
182 std::vector<vpImagePoint> cpyListEdgePoints = listEdgePoints;
183 std::sort(cpyListEdgePoints.begin(), cpyListEdgePoints.end(), sortImagePoints);
184 std::vector<vpImagePoint>::iterator last = std::unique(cpyListEdgePoints.begin(), cpyListEdgePoints.end());
185 static_cast<void>(cpyListEdgePoints.erase(last, cpyListEdgePoints.end()));
186 if (listEdgePoints.size() != cpyListEdgePoints.size()) {
190 std::vector<vpImagePoint>::iterator
start = listEdgePoints.begin();
191 std::vector<vpImagePoint>::iterator stop = listEdgePoints.end();
192 for (std::vector<vpImagePoint>::iterator it = start; it != stop; ++it) {
193 if (I_canny_visp[
static_cast<unsigned int>(it->get_i())][
static_cast<unsigned int>(it->get_j())] != 255) {
198 unsigned int nbRows = I_canny_visp.
getRows();
199 unsigned int nbCols = I_canny_visp.
getCols();
200 for (
unsigned int i = 0;
i < nbRows; ++
i) {
201 for (
unsigned int j = 0;
j < nbCols; ++
j) {
202 if (I_canny_visp[i][j] == 255) {
204 std::vector<vpImagePoint>::iterator
idx = std::find(listEdgePoints.begin(), listEdgePoints.end(), searchedPoint);
205 if (idx == listEdgePoints.end()) {
212 std::cout <<
"All the edge-point list tests went well !" << std::endl;
217 std::cout <<
"NAME" << std::endl;
218 std::cout << softName <<
": software to test the vpCannyEdgeComputation class and vpImageFilter::canny method" << std::endl;
219 std::cout <<
"SYNOPSIS" << std::endl;
220 std::cout <<
"\t" << softName
221 <<
" [-i, --image <pathToImg>]"
222 <<
" [-g, --gradient <kernelSize stdev>]"
223 <<
" [-t, --thresh <lowerThresh upperThresh>]"
224 <<
" [-a, --aperture <apertureSize>]"
225 <<
" [-f, --filter <filterName>]"
226 <<
" [-r, --ratio <lowerThreshRatio upperThreshRatio>]"
227 <<
" [-b, --backend <backendName>]"
228 <<
" [-n, --nb-threads <number of threads>]"
229 <<
" [-e, --edge-list]" << std::endl
230 <<
" [-h, --help]" << std::endl
232 std::cout <<
"DESCRIPTION" << std::endl;
233 std::cout <<
"\t-i, --image <pathToImg>" << std::endl
234 <<
"\t\tPermits to load an image on which will be tested the vpCanny class." << std::endl
235 <<
"\t\tWhen empty uses a simulated image." << std::endl
237 std::cout <<
"\t-g, --gradient <kernelSize stdev>" << std::endl
238 <<
"\t\tPermits to compute the gradients of the image outside the vpCanny class." << std::endl
239 <<
"\t\tFirst parameter is the size of the Gaussian kernel used to compute the gradients." << std::endl
240 <<
"\t\tSecond parameter is the standard deviation of the Gaussian kernel used to compute the gradients." << std::endl
243 std::cout <<
"\t-t, --thresh <lowerThresh upperThresh>" << std::endl
244 <<
"\t\tPermits to set the lower and upper thresholds of the vpCanny class." << std::endl
245 <<
"\t\tFirst parameter is the lower threshold." << std::endl
246 <<
"\t\tSecond parameter is the upper threshold." << std::endl
247 <<
"\t\tWhen set to -1 thresholds are computed automatically." << std::endl
250 std::cout <<
"\t-a, --aperture <apertureSize>" << std::endl
251 <<
"\t\tPermits to set the size of the gradient filter kernel." << std::endl
252 <<
"\t\tParameter must be odd and positive." << std::endl
255 std::cout <<
"\t-f, --filter <filterName>" << std::endl
256 <<
"\t\tPermits to choose the type of filter to apply to compute the gradient." << std::endl
260 std::cout <<
"\t-r, --ratio <lowerThreshRatio upperThreshRatio>" << std::endl
261 <<
"\t\tPermits to set the lower and upper thresholds ratio of the vpCanny class." << std::endl
262 <<
"\t\tFirst parameter is the lower threshold ratio." << std::endl
263 <<
"\t\tSecond parameter is the upper threshold ratio." << std::endl
266 std::cout <<
"\t-b, --backend <backendName>" << std::endl
267 <<
"\t\tPermits to use the vpImageFilter::canny method for comparison." << std::endl
271 std::cout <<
"\t-n, --nb-threads <number of threads>" << std::endl
272 <<
"\t\tPermits to choose the number of threads to use for the Canny." << std::endl
273 <<
"\t\tUse -1 to automatically choose the highest possible number of threads." << std::endl
274 <<
"\t\tDefault: " << options.
m_nbThread << std::endl
276 std::cout <<
"\t-e, --edge-list" << std::endl
277 <<
"\t\tPermits to save the edge list." << std::endl
278 <<
"\t\tDefault: OFF" << std::endl
280 std::cout <<
"\t-h, --help" << std::endl
281 <<
"\t\tPermits to display the different arguments this software handles." << std::endl
285int main(
int argc,
const char *argv[])
288 for (
int i = 1;
i < argc;
i++) {
289 std::string argv_str = std::string(argv[i]);
290 if ((argv_str ==
"-i" || argv_str ==
"--image") && i + 1 < argc) {
291 options.
m_img = std::string(argv[i + 1]);
294 else if ((argv_str ==
"-g" || argv_str ==
"--gradient") && i + 2 < argc) {
300 else if ((argv_str ==
"-t" || argv_str ==
"--thresh") && i + 2 < argc) {
301 options.
m_lowerThresh =
static_cast<float>(atof(argv[i + 1]));
302 options.
m_upperThresh =
static_cast<float>(atof(argv[i + 2]));
305 else if ((argv_str ==
"-a" || argv_str ==
"--aperture") && i + 1 < argc) {
309 else if ((argv_str ==
"-f" || argv_str ==
"--filter") && i + 1 < argc) {
313 else if ((argv_str ==
"-r" || argv_str ==
"--ratio") && i + 2 < argc) {
318 else if ((argv_str ==
"-b" || argv_str ==
"--backend") && i + 1 < argc) {
323 else if ((argv_str ==
"-n" || argv_str ==
"--nb-threads") && i + 1 < argc) {
327 else if ((argv_str ==
"-e") || (argv_str ==
"--edge-list")) {
330 else if (argv_str ==
"-h" || argv_str ==
"--help") {
331 usage(std::string(argv[0]), options);
335 std::cerr <<
"Argument \"" << argv_str <<
"\" is unknown." << std::endl;
340 std::string configAsTxt(
"Canny Configuration:\n");
342#if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
343 configAsTxt +=
"\tGaussian filter kernel size = " + std::to_string(options.
m_gaussianKernelSize) +
"\n";
344 configAsTxt +=
"\tGaussian filter standard deviation = " + std::to_string(options.
m_gaussianStdev) +
"\n";
345 configAsTxt +=
"\tGradient filter kernel size = " + std::to_string(options.
m_apertureSize) +
"\n";
346 configAsTxt +=
"\tCanny edge filter thresholds = [" + std::to_string(options.
m_lowerThresh) +
" ; " + std::to_string(options.
m_upperThresh) +
"]\n";
347 configAsTxt +=
"\tCanny edge filter thresholds ratio (for auto-thresholding) = [" + std::to_string(options.
m_lowerThreshRatio) +
" ; " + std::to_string(options.
m_upperThreshRatio) +
"]\n";
348 configAsTxt +=
"\tCanny edge filter nb threads = " + (options.
m_nbThread > 0 ? std::to_string(options.
m_nbThread) : std::string(
"auto")) +
"\n";
351 std::stringstream ss;
353 ss <<
"\tGaussian filter standard deviation = " << options.
m_gaussianStdev <<
"\n";
354 ss <<
"\tGradient filter kernel size = " << options.
m_apertureSize <<
"\n";
357 ss <<
"\tCanny edge filter nb threads = ";
365 configAsTxt += ss.str();
368 std::cout << configAsTxt << std::endl;
375 if (!options.
m_img.empty()) {
381 I_canny_input.
resize(720, 1280, 0);
382 for (
unsigned int r = 150;
r < 350;
r++) {
383 for (
unsigned int c = 150; c < 350; c++) {
384 I_canny_input[
r][c] = 125;
390 I_canny_visp = I_canny_imgFilter = dIx_uchar = dIy_uchar = I_canny_input;
399 p_IcannyImgFilter = &I_canny_imgFilter;
401 drawingHelpers::init(I_canny_input, I_canny_visp, p_dIx, p_dIy, p_IcannyImgFilter);
409 I_canny_visp = cannyDetector.
detect(I_canny_input);
411 std::cout <<
"Time to compute the edge-map: " << (tEnd - tStart) / 1000. <<
" ms" << std::endl;
413 float mean, max, stdev;
414 computeMeanMaxStdev(I_canny_input, mean, max, stdev);
416 checkEdgeList(cannyDetector, I_canny_visp);
418#if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
419 std::string title(
"Input of the Canny edge detector. Mean = " + std::to_string(mean) +
"+/-" + std::to_string(stdev) +
" Max = " + std::to_string(max));
423 std::stringstream ss;
424 ss <<
"Input of the Canny edge detector. Mean = " << mean <<
"+/-" << stdev <<
" Max = " << max;
428 drawingHelpers::display(I_canny_input, title);
429 drawingHelpers::display(I_canny_visp,
"Canny results on image " + options.
m_img);
440 drawingHelpers::waitForClick(I_canny_input,
true);
Class that implements the Canny's edge detector. It is possible to use a boolean mask to ignore some ...
vpImage< unsigned char > detect(const vpImage< vpRGBa > &I_color)
Detect the edges in an image. Convert the color image into a gray-scale image.
const std::vector< vpImagePoint > & getEdgePointsList() const
Get the list of edge-points that have been detected.
void setNbThread(const int &maxNbThread)
void setGradients(const vpImage< float > &dIx, const vpImage< float > &dIy)
Set the Gradients of the image that will be processed.
error that can be emitted by ViSP classes.
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Various image filter, convolution, etc...
static std::string vpCannyBackendTypeToString(const vpCannyBackendType &type)
Cast a vpImageFilter::vpCannyBackendTypeToString into a string, to know its name.
static void canny(const vpImage< unsigned char > &I, vpImage< unsigned char > &Ic, const unsigned int &gaussianFilterSize, const float &thresholdCanny, const unsigned int &apertureSobel)
static std::string vpCannyBackendTypeList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyBackendType.
static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
vpCannyFilteringAndGradientType
Canny filter and gradient operators to apply on the image before the edge detection stage.
vpCannyBackendType
Canny filter backends for the edge detection operations.
static vpCannyFilteringAndGradientType vpCannyFiltAndGradTypeFromStr(const std::string &name)
Cast a string into a vpImageFilter::vpCannyFilteringAndGradientType.
static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyBackendTypeToString.
static std::string vpGetCannyFiltAndGradTypes(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyFilteringAndGradientType.
static void computePartialDerivatives(const cv::Mat &cv_I, cv::Mat &cv_dIx, cv::Mat &cv_dIy, const bool &computeDx=true, const bool &computeDy=true, const bool &normalize=true, const unsigned int &gaussianKernelSize=5, const float &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING)
Compute the partial derivatives (i.e. horizontal and vertical gradients) of the input image.
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
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.
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
unsigned int getCols() const
unsigned int getRows() const
static bool equal(double x, double y, double threshold=0.001)
VISP_EXPORT double measureTimeMicros()
bool m_gradientOutsideClass
vpImageFilter::vpCannyFilteringAndGradientType m_filteringType
bool m_useVpImageFilterCanny
vpImageFilter::vpCannyBackendType m_backend