34 #include <visp3/core/vpCannyEdgeDetection.h>
35 #include <visp3/core/vpImageFilter.h>
36 #include <visp3/core/vpIoTools.h>
37 #include <visp3/core/vpRGBa.h>
48 const std::string &suf)
50 std::string list(pref);
73 name =
"opencv-backend";
76 name =
"visp-backend";
80 return "unknown-backend";
98 while ((i < count) && notFound) {
118 const std::string &suf)
120 std::string list(pref);
143 name =
"gaussianblur+sobel-filtering";
146 name =
"gaussianblur+scharr-filtering";
150 return "unknown-filtering";
166 bool notFound =
true;
168 while ((i < count) && notFound) {
267 const unsigned int size = kernelH.
size(), sizeV = kernelV.
size();
269 const unsigned int half_size = size / 2;
271 If.
resize(heightI, widthI, 0.0);
274 for (
unsigned int i = 0; i < heightI; ++i) {
275 for (
unsigned int j = half_size; j < (widthI - half_size); ++j) {
277 for (
unsigned int a = 0; a < size; ++a) {
278 conv += kernelH[a] *
static_cast<double>(I[i][j + half_size - a]);
281 I_filter[i][j] = conv;
285 for (
unsigned int i = half_size; i < (heightI - half_size); ++i) {
286 for (
unsigned int j = 0; j < widthI; ++j) {
288 for (
unsigned int a = 0; a < sizeV; ++a) {
289 conv += kernelV[a] * I_filter[i + half_size - a][j];
321 const unsigned int stop1J = (size - 1) / 2;
322 const unsigned int stop2J = widthI - (size - 1) / 2;
323 resizeAndInitializeIfNeeded(p_mask, heightI, widthI, dIx);
325 for (
unsigned int i = 0; i < heightI; i++) {
326 for (
unsigned int j = 0; j < stop1J; ++j) {
329 bool computeVal = checkBooleanMask(p_mask, i, j);
331 dIx[i][j].R =
static_cast<unsigned char>(vpImageFilter::filterXLeftBorderR(I, i, j,
filter, size));
332 dIx[i][j].G =
static_cast<unsigned char>(vpImageFilter::filterXLeftBorderG(I, i, j,
filter, size));
333 dIx[i][j].B =
static_cast<unsigned char>(vpImageFilter::filterXLeftBorderB(I, i, j,
filter, size));
336 for (
unsigned int j = stop1J; j < stop2J; ++j) {
339 bool computeVal = checkBooleanMask(p_mask, i, j);
346 for (
unsigned int j = stop2J; j < widthI; ++j) {
349 bool computeVal = checkBooleanMask(p_mask, i, j);
351 dIx[i][j].R =
static_cast<unsigned char>(vpImageFilter::filterXRightBorderR(I, i, j,
filter, size));
352 dIx[i][j].G =
static_cast<unsigned char>(vpImageFilter::filterXRightBorderG(I, i, j,
filter, size));
353 dIx[i][j].B =
static_cast<unsigned char>(vpImageFilter::filterXRightBorderB(I, i, j,
filter, size));
383 const unsigned int stop1I = (size - 1) / 2;
384 const unsigned int stop2I = heightI - (size - 1) / 2;
385 resizeAndInitializeIfNeeded(p_mask, heightI, widthI, dIy);
387 for (
unsigned int i = 0; i < stop1I; ++i) {
388 for (
unsigned int j = 0; j < widthI; ++j) {
391 bool computeVal = checkBooleanMask(p_mask, i, j);
393 dIy[i][j].R =
static_cast<unsigned char>(vpImageFilter::filterYTopBorderR(I, i, j,
filter, size));
394 dIy[i][j].G =
static_cast<unsigned char>(vpImageFilter::filterYTopBorderG(I, i, j,
filter, size));
395 dIy[i][j].B =
static_cast<unsigned char>(vpImageFilter::filterYTopBorderB(I, i, j,
filter, size));
399 for (
unsigned int i = stop1I; i < stop2I; ++i) {
400 for (
unsigned int j = 0; j < widthI; ++j) {
403 bool computeVal = checkBooleanMask(p_mask, i, j);
405 dIy[i][j].R =
static_cast<unsigned char>(vpImageFilter::filterYR(I, i, j,
filter, size));
406 dIy[i][j].G =
static_cast<unsigned char>(vpImageFilter::filterYG(I, i, j,
filter, size));
407 dIy[i][j].B =
static_cast<unsigned char>(vpImageFilter::filterYB(I, i, j,
filter, size));
411 for (
unsigned int i = stop2I; i < heightI; ++i) {
412 for (
unsigned int j = 0; j < widthI; ++j) {
415 bool computeVal = checkBooleanMask(p_mask, i, j);
417 dIy[i][j].R =
static_cast<unsigned char>(vpImageFilter::filterYBottomBorderR(I, i, j,
filter, size));
418 dIy[i][j].G =
static_cast<unsigned char>(vpImageFilter::filterYBottomBorderG(I, i, j,
filter, size));
419 dIy[i][j].B =
static_cast<unsigned char>(vpImageFilter::filterYBottomBorderB(I, i, j,
filter, size));
458 double *fg =
new double[(size + 1) / 2];
471 void vpImageFilter::getGaussianKernel<float>(
float *filter,
unsigned int size,
float sigma,
bool normalize);
474 void vpImageFilter::getGaussianDerivativeKernel<float>(
float *filter,
unsigned int size,
float sigma,
bool normalize);
477 void vpImageFilter::getGaussianDerivativeKernel<double>(
double *filter,
unsigned int size,
double sigma,
bool normalize);
517 const float *gaussianDerivativeKernel,
unsigned int size,
const vpImage<bool> *p_mask);
521 const double *gaussianDerivativeKernel,
unsigned int size,
const vpImage<bool> *p_mask);
525 const float *gaussianDerivativeKernel,
unsigned int size,
const vpImage<bool> *p_mask);
529 const double *gaussianDerivativeKernel,
unsigned int size,
const vpImage<bool> *p_mask);
533 const float *gaussianDerivativeKernel,
unsigned int size,
const vpImage<bool> *p_mask);
537 const double *gaussianDerivativeKernel,
unsigned int size,
const vpImage<bool> *p_mask);
541 const float *gaussianDerivativeKernel,
unsigned int size,
const vpImage<bool> *p_mask);
545 const double *gaussianDerivativeKernel,
unsigned int size,
const vpImage<bool> *p_mask);
554 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
555 #if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
556 cv::Mat imgsrc, imgdest;
558 cv::pyrDown(imgsrc, imgdest, cv::Size((
int)I.
getWidth() / 2, (
int)I.
getHeight() / 2));
561 cv::Mat imgsrc, imgdest;
563 cv::pyrDown(imgsrc, imgdest, cvSize((
int)I.
getWidth() / 2, (
int)I.
getHeight() / 2));
574 const unsigned int w = I.
getWidth() / 2;
575 const unsigned int height = I.
getHeight();
578 for (
unsigned int i = 0; i < height; ++i) {
580 for (
unsigned int j = 1; j < (w - 1); ++j) {
583 GI[i][w - 1] = I[i][2 * w - 1];
589 const unsigned int h = I.
getHeight() / 2;
590 const unsigned int width = I.
getWidth();
593 for (
unsigned int j = 0; j < width; ++j) {
595 for (
unsigned int i = 1; i < (h - 1); ++i) {
598 GI[h - 1][j] = I[2 * h - 1][j];
606 double vpImageFilter::getSobelKernelX<double>(
double *filter,
unsigned int size);
609 float vpImageFilter::getSobelKernelX<float>(
float *filter,
unsigned int size);
612 double vpImageFilter::getSobelKernelY<double>(
double *filter,
unsigned int size);
615 float vpImageFilter::getSobelKernelY<float>(
float *filter,
unsigned int size);
621 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
629 float m = (channel.rows * channel.cols) / 2.f;
634 float range[] = { 0, 256 };
635 const float *histRange = { range };
637 bool accumulate =
false;
639 cv::calcHist(&channel, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
642 while ((i < histSize) && (med < 0.0f)) {
643 bin += cvRound(hist.at<
float>(i));
644 if ((bin > m) && (med < 0.0f)) {
645 med =
static_cast<float>(i);
679 std::vector<cv::Mat> channels;
680 cv::split(cv_I_bgr, channels);
681 std::vector<float> meds(3);
682 const int orderMeds[] = { 2, 1, 0 };
683 const int orderCvChannels[] = { 0, 1, 2 };
684 for (
unsigned int i = 0; i < 3; ++i) {
685 meds[orderMeds[i]] =
median(channels[orderCvChannels[i]]);
709 float &lowerThresh,
const unsigned int &gaussianKernelSize,
710 const float &gaussianStdev,
const unsigned int &apertureGradient,
711 const float &lowerThresholdRatio,
const float &upperThresholdRatio,
714 double w = cv_I.cols;
715 double h = cv_I.rows;
717 cv::Mat dI, dIx, dIy, dIx_abs, dIy_abs;
719 if ((p_cv_dIx ==
nullptr) || (p_cv_dIy ==
nullptr)) {
729 cv::convertScaleAbs(dIx, dIx_abs);
730 cv::convertScaleAbs(dIy, dIy_abs);
731 cv::addWeighted(dIx_abs, 1, dIy_abs, 1, 0, dI);
732 dI.convertTo(dI, CV_8U);
736 const float range[] = { 0.f, 256.f };
737 const float *ranges[] = { range };
738 int channels[] = { 0 };
740 int histSize[] = { bins };
742 bool accumulate =
false;
743 cv::calcHist(&dI, 1, channels, cv::Mat(), hist, dims, histSize, ranges, uniform, accumulate);
745 float t =
static_cast<float>(upperThresholdRatio * w * h);
747 for (
int i = 0; i < bins; ++i) {
748 float tf = hist.at<
float>(i);
751 bon =
static_cast<float>(i);
755 float upperThresh = std::max<float>(bon, 1.f);
756 lowerThresh = lowerThresholdRatio * bon;
777 cv::Mat &cv_dIx, cv::Mat &cv_dIy,
778 const bool &computeDx,
const bool &computeDy,
const bool &normalize,
779 const unsigned int &gaussianKernelSize,
const float &gaussianStdev,
780 const unsigned int &apertureGradient,
787 cv::Size gsz(gaussianKernelSize, gaussianKernelSize);
788 cv::GaussianBlur(cv_I, img_blur, gsz, gaussianStdev);
795 if (apertureGradient > 3) {
796 scale *= std::pow(1. / 2., (
static_cast<double>(apertureGradient) * 2. - 3.));
800 cv::Sobel(img_blur, cv_dIx, CV_16S, 1, 0, apertureGradient, scale, 0., cv::BORDER_REPLICATE);
803 cv::Sobel(img_blur, cv_dIy, CV_16S, 0, 1, apertureGradient, scale, 0., cv::BORDER_REPLICATE);
812 cv::Scharr(img_blur, cv_dIx, CV_16S, 1, 0, scale);
815 cv::Scharr(img_blur, cv_dIy, CV_16S, 0, 1, scale);
828 const bool &computeDx,
const bool &computeDy,
const bool &normalize,
829 const unsigned int &gaussianKernelSize,
const float &gaussianStdev,
830 const unsigned int &apertureGradient,
831 const vpCannyFilteringAndGradientType &filteringType,
832 const vpCannyBackendType &backend,
const vpImage<bool> *p_mask);
837 const bool &computeDx,
const bool &computeDy,
const bool &normalize,
838 const unsigned int &gaussianKernelSize,
const double &gaussianStdev,
839 const unsigned int &apertureGradient,
840 const vpCannyFilteringAndGradientType &filteringType,
841 const vpCannyBackendType &backend,
const vpImage<bool> *p_mask);
844 void vpImageFilter::computePartialDerivatives<float, float>(
const vpImage<float> &I,
846 const bool &computeDx,
const bool &computeDy,
const bool &normalize,
847 const unsigned int &gaussianKernelSize,
const float &gaussianStdev,
848 const unsigned int &apertureGradient,
849 const vpCannyFilteringAndGradientType &filteringType,
850 const vpCannyBackendType &backend,
const vpImage<bool> *p_mask);
853 void vpImageFilter::computePartialDerivatives<float, double>(
const vpImage<float> &I,
855 const bool &computeDx,
const bool &computeDy,
const bool &normalize,
856 const unsigned int &gaussianKernelSize,
const double &gaussianStdev,
857 const unsigned int &apertureGradient,
858 const vpCannyFilteringAndGradientType &filteringType,
859 const vpCannyBackendType &backend,
const vpImage<bool> *p_mask);
862 void vpImageFilter::computePartialDerivatives<double, float>(
const vpImage<double> &I,
864 const bool &computeDx,
const bool &computeDy,
const bool &normalize,
865 const unsigned int &gaussianKernelSize,
const float &gaussianStdev,
866 const unsigned int &apertureGradient,
867 const vpCannyFilteringAndGradientType &filteringType,
868 const vpCannyBackendType &backend,
const vpImage<bool> *p_mask);
871 void vpImageFilter::computePartialDerivatives<double, double>(
const vpImage<double> &I,
873 const bool &computeDx,
const bool &computeDy,
const bool &normalize,
874 const unsigned int &gaussianKernelSize,
const double &gaussianStdev,
875 const unsigned int &apertureGradient,
876 const vpCannyFilteringAndGradientType &filteringType,
877 const vpCannyBackendType &backend,
const vpImage<bool> *p_mask);
882 const unsigned int &gaussianKernelSize,
883 const double &gaussianStdev,
const unsigned int &apertureGradient,
884 const float &lowerThresholdRatio,
const float &upperThresholdRatio,
891 const unsigned int &gaussianKernelSize,
892 const float &gaussianStdev,
const unsigned int &apertureGradient,
893 const float &lowerThresholdRatio,
const float &upperThresholdRatio,
940 const unsigned int &gaussianFilterSize,
const float &thresholdCanny,
941 const unsigned int &apertureSobel)
943 vpImageFilter::canny(Isrc, Ires, gaussianFilterSize, thresholdCanny / 3.f, thresholdCanny, apertureSobel);
990 const unsigned int &gaussianFilterSize,
991 const float &lowerThreshold,
const float &upperThreshold,
992 const unsigned int &apertureSobel)
994 const float gaussianStdev = 2.f;
995 const float upperThresholdRatio = 0.8f;
996 const float lowerThresholdRatio = 0.6f;
997 #if defined(HAVE_OPENCV_IMGPROC)
1003 canny(Isrc, Ires, gaussianFilterSize, lowerThreshold, upperThreshold, apertureSobel,
1004 gaussianStdev, lowerThresholdRatio, upperThresholdRatio,
false, cannyBackend, cannyFilteringSteps);
1072 const unsigned int &gaussianFilterSize,
1073 const float &lowerThreshold,
const float &upperThreshold,
const unsigned int &apertureGradient,
1074 const float &gaussianStdev,
const float &lowerThresholdRatio,
const float &upperThresholdRatio,
1075 const bool &normalizeGradients,
1080 #if defined(HAVE_OPENCV_IMGPROC)
1081 cv::Mat img_cvmat, cv_dx, cv_dy, edges_cvmat;
1084 gaussianStdev, apertureGradient, cannyFilteringSteps);
1085 float upperCannyThresh = upperThreshold;
1086 float lowerCannyThresh = lowerThreshold;
1087 if (upperCannyThresh < 0.f) {
1088 upperCannyThresh =
computeCannyThreshold(img_cvmat, &cv_dx, &cv_dy, lowerCannyThresh, gaussianFilterSize,
1089 gaussianStdev, apertureGradient, lowerThresholdRatio, upperThresholdRatio,
1090 cannyFilteringSteps);
1092 else if (lowerCannyThresh < 0.f) {
1093 lowerCannyThresh = upperCannyThresh / 3.f;
1095 #if (VISP_HAVE_OPENCV_VERSION >= 0x030200)
1096 cv::Canny(cv_dx, cv_dy, edges_cvmat, lowerCannyThresh, upperCannyThresh,
false);
1098 cv::GaussianBlur(img_cvmat, img_cvmat, cv::Size((
int)gaussianFilterSize, (
int)gaussianFilterSize),
1099 gaussianStdev, gaussianStdev);
1100 cv::Canny(img_cvmat, edges_cvmat, lowerCannyThresh, upperCannyThresh);
1104 std::string errMsg(
"[vpImageFilter::canny]You asked for CANNY_OPENCV_BACKEND but ViSP has not been compiled with OpenCV");
1109 float upperCannyThresh = upperThreshold;
1110 float lowerCannyThresh = lowerThreshold;
1114 gaussianStdev, apertureGradient, cannyFilteringSteps, cannyBackend, p_mask);
1116 if (upperCannyThresh < 0.f) {
1117 upperCannyThresh =
computeCannyThreshold(Isrc, lowerCannyThresh, &dIx, &dIy, gaussianFilterSize, gaussianStdev,
1118 apertureGradient, lowerThresholdRatio, upperThresholdRatio,
1119 cannyFilteringSteps, p_mask);
1121 else if (lowerCannyThresh < 0.f) {
1122 lowerCannyThresh = upperCannyThresh / 3.f;
1124 vpCannyEdgeDetection edgeDetector(gaussianFilterSize, gaussianStdev, apertureGradient, lowerCannyThresh, upperCannyThresh,
1125 lowerThresholdRatio, upperThresholdRatio, cannyFilteringSteps);
1128 Ires = edgeDetector.
detect(Isrc);
unsigned int size() const
Return the number of elements of the 2D array.
vpImage< unsigned char > detect(const vpImage< vpRGBa > &I_color)
Detect the edges in an image. Convert the color image into a gray-scale image.
void setMask(const vpImage< bool > *p_mask)
Set a mask to ignore pixels for which the mask is false.
void setGradients(const vpImage< float > &dIx, const vpImage< float > &dIy)
Set the Gradients of the image that will be processed.
Implementation of column vector and the associated operations.
error that can be emitted by ViSP classes.
@ badValue
Used to indicate that a value is not in the allowed range.
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
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 unsigned char filterGaussXPyramidal(const vpImage< unsigned char > &I, unsigned int i, unsigned int j)
static void filterXB(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
static void sepFilter(const vpImage< unsigned char > &I, vpImage< double > &If, const vpColVector &kernelH, const vpColVector &kernelV)
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 vpCannyFilteringAndGradientType vpCannyFilteringAndGradientTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyFilteringAndGradientType.
static void gaussianBlur(const vpImage< ImageType > &I, vpImage< FilterType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true, const vpImage< bool > *p_mask=nullptr)
static void filter(const vpImage< ImageType > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false, const vpImage< bool > *p_mask=nullptr)
static std::string vpCannyFilteringAndGradientTypeToString(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
vpCannyBackendType
Canny filter backends for the edge detection operations.
@ CANNY_VISP_BACKEND
Use ViSP.
@ CANNY_OPENCV_BACKEND
Use OpenCV.
static std::string vpCannyBackendTypeList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyBackendType.
static void filterXR(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
static float computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy, float &lowerThresh, const unsigned int &gaussianKernelSize=5, const float &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const float &lowerThresholdRatio=0.6, const float &upperThresholdRatio=0.8, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING)
Compute the upper Canny edge filter threshold, using Gaussian blur + Sobel or + Scharr operators to c...
static void getGaussXPyramidal(const vpImage< unsigned char > &I, vpImage< unsigned char > &GI)
static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static void getGaussYPyramidal(const vpImage< unsigned char > &I, vpImage< unsigned char > &GI)
static float median(const cv::Mat &cv_I)
Calculates the median value of a single channel. The algorithm is based on based on https://github....
static std::string vpCannyFilteringAndGradientTypeList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyFilteringAndGradientType.
static void filterY(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyBackendTypeToString.
static void filterXG(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
static void filterX(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static unsigned char filterGaussYPyramidal(const vpImage< unsigned char > &I, unsigned int i, unsigned int j)
static void getGaussPyramidal(const vpImage< unsigned char > &I, vpImage< unsigned char > &GI)
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.
void destroy()
Destructor : Memory de-allocation.
unsigned int getWidth() const
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
unsigned int getHeight() const