33 #include <visp3/core/vpConfig.h>
35 #include <visp3/core/vpCannyEdgeDetection.h>
36 #include <visp3/core/vpImageFilter.h>
37 #include <visp3/io/vpImageIo.h>
39 #ifdef HAVE_OPENCV_IMGPROC
40 #include <opencv2/imgproc/imgproc.hpp>
43 #include "drawingHelpers.h"
45 #ifdef ENABLE_VISP_NAMESPACE
67 , m_gradientOutsideClass(false)
68 , m_useVpImageFilterCanny(false)
69 , m_saveEdgeList(false)
70 , m_gaussianKernelSize(3)
76 , m_lowerThreshRatio(0.6f)
77 , m_upperThreshRatio(0.8f)
78 #ifdef VISP_HAVE_OPENCV
87 void computeMeanMaxStdev(
const vpImage<T> &I,
float &mean,
float &max,
float &stdev)
89 max = std::numeric_limits<float>::epsilon();
92 unsigned int nbRows = I.
getRows();
93 unsigned int nbCols = I.
getCols();
94 float scale = 1.f / (
static_cast<float>(nbRows) *
static_cast<float>(nbCols));
95 for (
unsigned int r = 0; r < nbRows; r++) {
96 for (
unsigned int c = 0; c < nbCols; c++) {
98 max = std::max<float>(max,
static_cast<float>(I[r][c]));
102 for (
unsigned int r = 0; r < nbRows; r++) {
103 for (
unsigned int c = 0; c < nbCols; c++) {
104 stdev += (I[r][c] - mean) * (I[r][c] - mean);
108 stdev = std::sqrt(stdev);
111 void setGradientOutsideClass(
const vpImage<unsigned char> &I,
const int &gaussianKernelSize,
const float &gaussianStdev,
120 apertureSize, filteringType);
126 float mean, max, stdev;
127 computeMeanMaxStdev(dIx, mean, max, stdev);
129 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
130 std::string title =
"Gradient along the horizontal axis. Mean = " + std::to_string(mean)
131 +
"+/-" + std::to_string(stdev) +
" Max = " + std::to_string(max);
135 std::stringstream ss;
136 ss <<
"Gradient along the horizontal axis. Mean = " << mean<<
"+/-" << stdev<<
" Max = " << max;
141 drawingHelpers::display(dIx_uchar, title);
142 computeMeanMaxStdev(dIy, mean, max, stdev);
143 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
144 title =
"Gradient along the horizontal axis. Mean = " + std::to_string(mean)
145 +
"+/-" + std::to_string(stdev) +
" Max = " + std::to_string(max);
148 std::stringstream ss;
149 ss <<
"Gradient along the horizontal axis. Mean = " << mean<<
"+/-" << stdev<<
" Max = " << max;
154 drawingHelpers::display(dIy_uchar, title);
172 std::vector<vpImagePoint> cpyListEdgePoints = listEdgePoints;
173 std::sort(cpyListEdgePoints.begin(), cpyListEdgePoints.end(), sortImagePoints);
174 std::vector<vpImagePoint>::iterator last = std::unique(cpyListEdgePoints.begin(), cpyListEdgePoints.end());
175 static_cast<void>(cpyListEdgePoints.erase(last, cpyListEdgePoints.end()));
176 if (listEdgePoints.size() != cpyListEdgePoints.size()) {
180 std::vector<vpImagePoint>::iterator start = listEdgePoints.begin();
181 std::vector<vpImagePoint>::iterator stop = listEdgePoints.end();
182 for (std::vector<vpImagePoint>::iterator it = start; it != stop; ++it) {
183 if (I_canny_visp[
static_cast<unsigned int>(it->get_i())][
static_cast<unsigned int>(it->get_j())] != 255) {
188 unsigned int nbRows = I_canny_visp.
getRows();
189 unsigned int nbCols = I_canny_visp.
getCols();
190 for (
unsigned int i = 0; i < nbRows; ++i) {
191 for (
unsigned int j = 0; j < nbCols; ++j) {
192 if (I_canny_visp[i][j] == 255) {
194 std::vector<vpImagePoint>::iterator idx = std::find(listEdgePoints.begin(), listEdgePoints.end(), searchedPoint);
195 if (idx == listEdgePoints.end()) {
202 std::cout <<
"All the edge-point list tests went well !" << std::endl;
207 std::cout <<
"NAME" << std::endl;
208 std::cout << softName <<
": software to test the vpCannyEdgeComputation class and vpImageFilter::canny method" << std::endl;
209 std::cout <<
"SYNOPSIS" << std::endl;
210 std::cout <<
"\t" << softName
211 <<
" [-i, --image <pathToImg>]"
212 <<
" [-g, --gradient <kernelSize stdev>]"
213 <<
" [-t, --thresh <lowerThresh upperThresh>]"
214 <<
" [-a, --aperture <apertureSize>]"
215 <<
" [-f, --filter <filterName>]"
216 <<
" [-r, --ratio <lowerThreshRatio upperThreshRatio>]"
217 <<
" [-b, --backend <backendName>]"
218 <<
" [-e, --edge-list]" << std::endl
219 <<
" [-h, --help]" << std::endl
221 std::cout <<
"DESCRIPTION" << std::endl;
222 std::cout <<
"\t-i, --image <pathToImg>" << std::endl
223 <<
"\t\tPermits to load an image on which will be tested the vpCanny class." << std::endl
224 <<
"\t\tWhen empty uses a simulated image." << std::endl
226 std::cout <<
"\t-g, --gradient <kernelSize stdev>" << std::endl
227 <<
"\t\tPermits to compute the gradients of the image outside the vpCanny class." << std::endl
228 <<
"\t\tFirst parameter is the size of the Gaussian kernel used to compute the gradients." << std::endl
229 <<
"\t\tSecond parameter is the standard deviation of the Gaussian kernel used to compute the gradients." << std::endl
232 std::cout <<
"\t-t, --thresh <lowerThresh upperThresh>" << std::endl
233 <<
"\t\tPermits to set the lower and upper thresholds of the vpCanny class." << std::endl
234 <<
"\t\tFirst parameter is the lower threshold." << std::endl
235 <<
"\t\tSecond parameter is the upper threshold." << std::endl
236 <<
"\t\tWhen set to -1 thresholds are computed automatically." << std::endl
239 std::cout <<
"\t-a, --aperture <apertureSize>" << std::endl
240 <<
"\t\tPermits to set the size of the gradient filter kernel." << std::endl
241 <<
"\t\tParameter must be odd and positive." << std::endl
244 std::cout <<
"\t-f, --filter <filterName>" << std::endl
245 <<
"\t\tPermits to choose the type of filter to apply to compute the gradient." << std::endl
249 std::cout <<
"\t-r, --ratio <lowerThreshRatio upperThreshRatio>" << std::endl
250 <<
"\t\tPermits to set the lower and upper thresholds ratio of the vpCanny class." << std::endl
251 <<
"\t\tFirst parameter is the lower threshold ratio." << std::endl
252 <<
"\t\tSecond parameter is the upper threshold ratio." << std::endl
255 std::cout <<
"\t-b, --backend <backendName>" << std::endl
256 <<
"\t\tPermits to use the vpImageFilter::canny method for comparison." << std::endl
260 std::cout <<
"\t-e, --edge-list" << std::endl
261 <<
"\t\tPermits to save the edge list." << std::endl
262 <<
"\t\tDefault: OFF" << std::endl
264 std::cout <<
"\t-h, --help" << std::endl
265 <<
"\t\tPermits to display the different arguments this software handles." << std::endl
269 int main(
int argc,
const char *argv[])
272 for (
int i = 1; i < argc; i++) {
273 std::string argv_str = std::string(argv[i]);
274 if ((argv_str ==
"-i" || argv_str ==
"--image") && i + 1 < argc) {
275 options.
m_img = std::string(argv[i + 1]);
278 else if ((argv_str ==
"-g" || argv_str ==
"--gradient") && i + 2 < argc) {
284 else if ((argv_str ==
"-t" || argv_str ==
"--thresh") && i + 2 < argc) {
285 options.
m_lowerThresh =
static_cast<float>(atof(argv[i + 1]));
286 options.
m_upperThresh =
static_cast<float>(atof(argv[i + 2]));
289 else if ((argv_str ==
"-a" || argv_str ==
"--aperture") && i + 1 < argc) {
293 else if ((argv_str ==
"-f" || argv_str ==
"--filter") && i + 1 < argc) {
297 else if ((argv_str ==
"-r" || argv_str ==
"--ratio") && i + 2 < argc) {
302 else if ((argv_str ==
"-b" || argv_str ==
"--backend") && i + 1 < argc) {
307 else if ((argv_str ==
"-e") || (argv_str ==
"--edge-list")) {
310 else if (argv_str ==
"-h" || argv_str ==
"--help") {
311 usage(std::string(argv[0]), options);
315 std::cerr <<
"Argument \"" << argv_str <<
"\" is unknown." << std::endl;
320 std::string configAsTxt(
"Canny Configuration:\n");
322 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
323 configAsTxt +=
"\tGaussian filter kernel size = " + std::to_string(options.
m_gaussianKernelSize) +
"\n";
324 configAsTxt +=
"\tGaussian filter standard deviation = " + std::to_string(options.
m_gaussianStdev) +
"\n";
325 configAsTxt +=
"\tGradient filter kernel size = " + std::to_string(options.
m_apertureSize) +
"\n";
326 configAsTxt +=
"\tCanny edge filter thresholds = [" + std::to_string(options.
m_lowerThresh) +
" ; " + std::to_string(options.
m_upperThresh) +
"]\n";
327 configAsTxt +=
"\tCanny edge filter thresholds ratio (for auto-thresholding) = [" + std::to_string(options.
m_lowerThreshRatio) +
" ; " + std::to_string(options.
m_upperThreshRatio) +
"]\n";
330 std::stringstream ss;
332 ss <<
"\tGaussian filter standard deviation = " << options.
m_gaussianStdev <<
"\n";
333 ss <<
"\tGradient filter kernel size = " << options.
m_apertureSize <<
"\n";
336 configAsTxt += ss.str();
339 std::cout << configAsTxt << std::endl;
345 if (!options.
m_img.empty()) {
351 I_canny_input.
resize(500, 500, 0);
352 for (
unsigned int r = 150; r < 350; r++) {
353 for (
unsigned int c = 150; c < 350; c++) {
354 I_canny_input[r][c] = 125;
360 I_canny_visp = I_canny_imgFilter = dIx_uchar = dIy_uchar = I_canny_input;
369 p_IcannyImgFilter = &I_canny_imgFilter;
371 drawingHelpers::init(I_canny_input, I_canny_visp, p_dIx, p_dIy, p_IcannyImgFilter);
378 I_canny_visp = cannyDetector.
detect(I_canny_input);
379 float mean, max, stdev;
380 computeMeanMaxStdev(I_canny_input, mean, max, stdev);
382 checkEdgeList(cannyDetector, I_canny_visp);
384 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
385 std::string title(
"Input of the Canny edge detector. Mean = " + std::to_string(mean) +
"+/-" + std::to_string(stdev) +
" Max = " + std::to_string(max));
389 std::stringstream ss;
390 ss <<
"Input of the Canny edge detector. Mean = " << mean <<
"+/-" << stdev <<
" Max = " << max;
394 drawingHelpers::display(I_canny_input, title);
395 drawingHelpers::display(I_canny_visp,
"Canny results on image " + options.
m_img);
406 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.
std::vector< vpImagePoint > getEdgePointsList() const
Get the list of edge-points that have been detected.
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)
bool m_gradientOutsideClass
vpImageFilter::vpCannyFilteringAndGradientType m_filteringType
bool m_useVpImageFilterCanny
vpImageFilter::vpCannyBackendType m_backend