33#include <visp3/core/vpConfig.h>
35#include <visp3/core/vpCannyEdgeDetection.h>
36#include <visp3/core/vpHSV.h>
37#include <visp3/core/vpImageConvert.h>
38#include <visp3/core/vpImageFilter.h>
39#include <visp3/core/vpIoTools.h>
40#include <visp3/core/vpRGBa.h>
41#include <visp3/gui/vpDisplayFactory.h>
42#include <visp3/io/vpImageIo.h>
44#ifdef ENABLE_VISP_NAMESPACE
48#if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_11)
49template <
typename FilterType>
55 min = std::numeric_limits<FilterType>::max();
56 for (
unsigned int r = 0;
r <
h; ++
r) {
57 for (
unsigned int c = 0; c <
w; ++c) {
58 GI[
r][c] = std::abs(GIx[r][c]) + std::abs(GIy[r][c]);
59 max = std::max(max, GI[r][c]);
60 min = std::min(min, GI[r][c]);
65template<
typename FilterType>
69 const FilterType range = max - min;
70 const FilterType
step = range /
static_cast<FilterType
>(256);
72 for (
unsigned int r = 0;
r <
h; ++
r) {
73 for (
unsigned int c = 0; c <
w; ++c) {
74 Idisp[
r][c] =
static_cast<unsigned char>(std::floor((GI[r][c] - min) / step));
105#ifdef VISP_HAVE_DISPLAY
117 std::cout <<
"NAME" << std::endl;
118 std::cout << softName <<
": software to test the vpCannyEdgeComputation class and vpImageFilter::canny method" << std::endl;
119 std::cout <<
"SYNOPSIS" << std::endl;
120 std::cout <<
"\t" << softName
121 <<
" [-i, --image <pathToImg>]"
122 <<
" [-g, --gradient <kernelSize stdev>]"
123 <<
" [-t, --thresh <lowerThresh upperThresh>]"
125 <<
" [-r, --ratio <lowerThreshRatio upperThreshRatio>]"
126 <<
" [-n, --nb-threads <number of threads>]"
127 <<
" [-s, --save]" << std::endl
128 <<
" [-d, --no-display]" << std::endl
129 <<
" [-m, --use-mask]" << std::endl
130 <<
" [-h, --help]" << std::endl
132 std::cout <<
"DESCRIPTION" << std::endl;
133 std::cout <<
"\t-i, --image <pathToImg>" << std::endl
134 <<
"\t\tPermits to load an image on which will be tested the vpCanny class." << std::endl
135 <<
"\t\tWhen empty uses a simulated image." << std::endl
137 std::cout <<
"\t-g, --gradient <kernelSize stdev>" << std::endl
138 <<
"\t\tPermits to compute the gradients of the image outside the vpCanny class." << std::endl
139 <<
"\t\tFirst parameter is the size of the Gaussian kernel used to compute the gradients." << std::endl
140 <<
"\t\tSecond parameter is the standard deviation of the Gaussian kernel used to compute the gradients." << std::endl
143 std::cout <<
"\t-t, --thresh <lowerThresh upperThresh>" << std::endl
144 <<
"\t\tPermits to set the lower and upper thresholds of the vpCanny class." << std::endl
145 <<
"\t\tFirst parameter is the lower threshold." << std::endl
146 <<
"\t\tSecond parameter is the upper threshold." << std::endl
147 <<
"\t\tWhen set to -1 thresholds are computed automatically." << std::endl
150 std::cout <<
"\t-r, --ratio <lowerThreshRatio upperThreshRatio>" << std::endl
151 <<
"\t\tPermits to set the lower and upper thresholds ratio of the vpCanny class." << std::endl
152 <<
"\t\tFirst parameter is the lower threshold ratio." << std::endl
153 <<
"\t\tSecond parameter is the upper threshold ratio." << std::endl
156 std::cout <<
"\t-f, --filter <filterName>" << std::endl
157 <<
"\t\tPermits to choose the type of filter to apply to compute the gradient." << std::endl
161 std::cout <<
"\t-n, --nb-threads <number of threads>" << std::endl
162 <<
"\t\tPermits to choose the number of threads to use for the Canny." << std::endl
163 <<
"\t\tUse -1 to automatically choose the highest possible number of threads." << std::endl
164 <<
"\t\tDefault: " << options.
m_nbThread << std::endl
166 std::cout <<
"\t-s, --save" << std::endl
167 <<
"\t\tPermits to save the different images." << std::endl
169 std::cout <<
" -d, --no-display" << std::endl
170 <<
" Deactivate display." << std::endl
171 <<
" Default: display is "
172#ifdef VISP_HAVE_DISPLAY
175 <<
"OFF" << std::endl
178 std::cout <<
" -m, --use-mask" << std::endl
179 <<
" If true, use a predifined boolean mask that determines which pixels should be considered and which should be ignored" << std::endl
181 std::cout <<
"\t-h, --help" << std::endl
182 <<
"\t\tPermits to display the different arguments this software handles." << std::endl
186int main(
int argc,
const char *argv[])
189 for (
int i = 1;
i < argc;
i++) {
190 std::string argv_str = std::string(argv[i]);
191 if ((argv_str ==
"-i" || argv_str ==
"--image") && i + 1 < argc) {
192 options.
m_img = std::string(argv[i + 1]);
195 else if ((argv_str ==
"-g" || argv_str ==
"--gradient") && i + 2 < argc) {
200 else if ((argv_str ==
"-t" || argv_str ==
"--thresh") && i + 2 < argc) {
201 options.
m_lowerThresh =
static_cast<float>(atof(argv[i + 1]));
202 options.
m_upperThresh =
static_cast<float>(atof(argv[i + 2]));
205 else if ((argv_str ==
"-r" || argv_str ==
"--ratio") && i + 2 < argc) {
210 else if ((argv_str ==
"-f" || argv_str ==
"--filter") && i + 1 < argc) {
214 else if ((argv_str ==
"-n" || argv_str ==
"--nb-threads") && i + 1 < argc) {
218 else if (argv_str ==
"-s" || argv_str ==
"--save") {
221 else if (argv_str ==
"-d" || argv_str ==
"--no-display") {
224 else if (argv_str ==
"-m" || argv_str ==
"--use-mask") {
227 else if (argv_str ==
"-h" || argv_str ==
"--help") {
232 std::cerr <<
"Argument \"" << argv_str <<
"\" is unknown." << std::endl;
237 std::string configAsTxt(
"Canny Configuration:\n");
239 configAsTxt +=
"\tGaussian filter kernel size = " + std::to_string(options.
m_gaussianKernelSize) +
"\n";
240 configAsTxt +=
"\tGaussian filter standard deviation = " + std::to_string(options.
m_gaussianStdev) +
"\n";
241 configAsTxt +=
"\tCanny edge filter thresholds = [" + std::to_string(options.
m_lowerThresh) +
" ; " + std::to_string(options.
m_upperThresh) +
"]\n";
242 configAsTxt +=
"\tCanny edge filter thresholds ratio (for auto-thresholding) = [" + std::to_string(options.
m_lowerThreshRatio) +
" ; " + std::to_string(options.
m_upperThreshRatio) +
"]\n";
243 configAsTxt +=
"\tCanny edge filter nb threads = " + (options.
m_nbThread > 0 ? std::to_string(options.
m_nbThread) : std::string(
"auto")) +
"\n";
244 std::cout << configAsTxt << std::endl;
246 unsigned int uselessAperture = 3;
252 if (!options.
m_img.empty()) {
257 std::cout <<
"This example only works on a real image. Please use the -i option." << std::endl;
264 int midHeight = Iload.
getRows()/2;
265 int midWidth = Iload.
getCols()/2;
267 for (
int c = 0; c <
width; ++c) {
268 mask[midHeight -
r][midWidth - c] =
true;
269 mask[midHeight +
r][midWidth - c] =
true;
270 mask[midHeight -
r][midWidth + c] =
true;
271 mask[midHeight +
r][midWidth + c] =
true;
276 cannyDetector.setMask(p_mask);
277 cannyDetector.setNbThread(options.
m_nbThread);
283 std::cout <<
"Time to convert RGBa into HSV uchar + compute the edge-map: " << (tEndHSVuc - tStartHSVuc) / 1000. <<
" ms" << std::endl;
287 cannyDetector.reinit();
288 cannyDetector.setMask(p_mask);
291 std::cout <<
"Time to convert RGBa into HSV double + compute the edge-map: " << (tEndHSVd - tStartHSVd) / 1000. <<
" ms" << std::endl;
295 cannyDetectorUC.setMask(p_mask);
296 cannyDetectorUC.setNbThread(options.
m_nbThread);
302 std::cout <<
"Time to convert RGBa into uchar + compute the edge-map for RGBa: " << (tEndChar - tStartChar) / 1000. <<
" ms" << std::endl;
305 using FilterType = float;
315 FilterType min = 0., max = 0.;
316 computeAbsoluteGradient(GIx, GIy, GI, min, max);
326 computeAbsoluteGradient(GIx, GIy, GI, min, max);
334 FilterType scaleX, scaleY;
348 for (
unsigned int r = 0;
r < array.
getRows(); ++
r) {
349 for (
unsigned int c = 0; c < array.
getCols(); ++c) {
350 array[
r][c] = array[
r][c] / scale;
354 scaleFilter(derFilterX, scaleX);
355 scaleFilter(derFilterY, scaleY);
361 computeAbsoluteGradient(GIx_uc, GIy_uc, GI_uc, min, max);
364 std::cout <<
"[vpHSV<uchar>]" << std::endl;
365 std::cout <<
"\tgblur = " << (tEndBlurHSVUC - tStartBlurHSVUC) / 1000. << std::endl;
366 std::cout <<
"\tgrad = " << (tEndGradientHSVUC - tStartGradientHSVUC) / 1000. << std::endl;
367 std::cout << std::endl;
369 std::cout <<
"[vpHSV<double>]" << std::endl;
370 std::cout <<
"\tgblur = " << (tEndBlurHSVd - tStartBlurHSVd) / 1000. << std::endl;
371 std::cout <<
"\tgrad = " << (tEndGradientHSVd - tStartGradientHSVd) / 1000. << std::endl;
372 std::cout << std::endl;
374 std::cout <<
"[uchar]" << std::endl;
375 std::cout <<
"\tgblur = " << (tEndBlurUC - tStartBlurUC) / 1000. << std::endl;
376 std::cout <<
"\tgrad = " << (tEndGradientUC - tStartGradientUC) / 1000. << std::endl;
377 std::cout << std::endl;
379#ifdef VISP_HAVE_DISPLAY
382 int posX = disp_input->getWidth() + 20;
383 int posY = disp_input->getHeight() + 20;
416 vpImageIo::write(GIdisp_hsvuc_imgfilter,
"Gradient_HSVUC_" + basename +
".jpg");
417 vpImageIo::write(GIdisp_hsvd_imgfilter,
"Gradient_HSVD_" + basename +
".jpg");
425 std::cout <<
"C++11 is needed to work with vpHSV." << std::endl;
Implementation of a generic 2D array used as base class for matrices and vectors.
unsigned int getCols() const
unsigned int getRows() const
Class that implements the Canny's edge detector. It is possible to use a boolean mask to ignore some ...
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
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)
error that can be emitted by ViSP classes.
@ notImplementedError
Not implemented.
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Various image filter, convolution, etc...
static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
static void gradientFilter(const vpImage< vpHSV< ArithmeticType, useFullScale > > &I, vpImage< FilterType > &GIx, vpImage< FilterType > &GIy, const int &nbThread=-1, const vpImage< bool > *p_mask=nullptr, const vpImageFilter::vpCannyFilteringAndGradientType &type=CANNY_GBLUR_SCHARR_FILTERING)
Compute the horizontal and vertical gradients for HSV images.
vpCannyFilteringAndGradientType
Canny filter and gradient operators to apply on the image before the edge detection stage.
@ CANNY_GBLUR_SOBEL_FILTERING
Apply Gaussian blur + Sobel operator on the input image.
@ CANNY_GBLUR_SCHARR_FILTERING
Apply Gaussian blur + Scharr operator on the input image.
static void filter(const vpImage< ImageType > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false, const vpImage< bool > *p_mask=nullptr)
static void gaussianBlur(const vpImage< ImageType > &I, vpImage< OutputType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true, const vpImage< bool > *p_mask=nullptr)
static vpCannyFilteringAndGradientType vpCannyFiltAndGradTypeFromStr(const std::string &name)
Cast a string into a vpImageFilter::vpCannyFilteringAndGradientType.
static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
static std::string vpGetCannyFiltAndGradTypes(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyFilteringAndGradientType.
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)
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.
unsigned int getWidth() const
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
unsigned int getCols() const
unsigned int getHeight() const
unsigned int getRows() const
std::shared_ptr< vpDisplay > createDisplay()
Return a smart pointer vpDisplay specialization if a GUI library is available or nullptr otherwise.
VISP_EXPORT double measureTimeMicros()
bool m_useDisplay
If true, activate the plot and the renderer if VISP_HAVE_DISPLAY is defined.
vpImageFilter::vpCannyFilteringAndGradientType m_filteringType
bool m_useMask
If true, use a predifined boolean mask that determines which pixels should be considered and which sh...