Skip to content

Commit

Permalink
[CORPS] Working with normalized Gaussian kernel + corrected Sobel sca…
Browse files Browse the repository at this point in the history
…le + normalize OpenCV gradients to make the computeCannyThresh works
  • Loading branch information
rlagneau committed Oct 18, 2023
1 parent eeeb992 commit 40de54c
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 25 deletions.
5 changes: 4 additions & 1 deletion modules/core/include/visp3/core/vpCannyEdgeDetection.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,13 @@ class VISP_EXPORT vpCannyEdgeDetection
* \param[in] upperThresholdRatio : If the thresholds must be computed,the upper threshold will be equal to the value
* such as the number of pixels of the image times \b upperThresholdRatio have an absolute gradient lower than the
* upper threshold.
* \param[in] filteringType : The filtering and gradient operators to apply to the image before the edge detection
* operation.
*/
vpCannyEdgeDetection(const int &gaussianKernelSize, const float &gaussianStdev, const unsigned int &sobelAperture,
const float &lowerThreshold = -1.f, const float &upperThreshold = -1.f,
const float &lowerThresholdRatio = 0.6f, const float &upperThresholdRatio = 0.8f);
const float &lowerThresholdRatio = 0.6f, const float &upperThresholdRatio = 0.8f,
const vpImageFilter::vpCannyFilteringAndGradientType &filteringType = vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING);

// // Configuration from files
#ifdef VISP_HAVE_NLOHMANN_JSON
Expand Down
21 changes: 13 additions & 8 deletions modules/core/include/visp3/core/vpImageFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ class VISP_EXPORT vpImageFilter
\tparam FilterType: Either float, to accelerate the computation time, or double, to have greater precision.
\param filter : Pointer to a double array already allocated.
\param size : Kernel size computed as: kernel_size = size*2 + 1 (max size is 20).
\return Scaling factor.
\return Scaling factor to normalize the Scharr kernel.
*/
template <typename FilterType>
inline static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
Expand All @@ -1035,7 +1035,7 @@ class VISP_EXPORT vpImageFilter
\tparam FilterType : Either float, to accelerate the computation time, or double, to have greater precision.
\param filter : Pointer to a double array already allocated.
\param size : Kernel size computed as: kernel_size = size*2 + 1 (max size is 20).
\return Scaling factor.
\return Scaling factor to normalize the Scharr kernel.
*/
template <typename FilterType>
inline static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
Expand Down Expand Up @@ -1063,7 +1063,7 @@ class VISP_EXPORT vpImageFilter
\tparam FilterType: Either float, to accelerate the computation time, or double, to have greater precision.
\param filter : Pointer to a double array already allocated.
\param size : Kernel size computed as: kernel_size = size*2 + 1 (max size is 20).
\return Scaling factor.
\return Scaling factor to normalize the Sobel kernel.
*/
template <typename FilterType>
inline static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
Expand All @@ -1084,7 +1084,7 @@ class VISP_EXPORT vpImageFilter
\tparam FilterType : Either float, to accelerate the computation time, or double, to have greater precision.
\param filter : Pointer to a double array already allocated.
\param size : Kernel size computed as: kernel_size = size*2 + 1 (max size is 20).
\return Scaling factor.
\return Scaling factor to normalize the Sobel kernel.
*/
template <typename FilterType>
inline static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
Expand Down Expand Up @@ -1113,28 +1113,33 @@ class VISP_EXPORT vpImageFilter
throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");

const unsigned int kernel_size = size * 2 + 1;
double scale = (1. / 8.); // Scale to normalize Sobel3x3
if (kernel_size == 3) {
memcpy(filter, SobelY3x3, kernel_size * kernel_size * sizeof(FilterType));
return 1 / 8.0;
return scale;
}
scale *= 1./ 16.; // Sobel5x5 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel3x3
if (kernel_size == 5) {
memcpy(filter, SobelY5x5, kernel_size * kernel_size * sizeof(FilterType));
return 1 / 16.0;
return scale;
}
scale *= 1./ 16.; // Sobel7x7 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel5x5
if (kernel_size == 7) {
memcpy(filter, SobelY7x7, kernel_size * kernel_size * sizeof(FilterType));
return 1 / 16.0;
return scale;
}

vpArray2D<FilterType> sobelY(7, 7);
memcpy(sobelY.data, SobelY7x7, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
for (unsigned int i = 4; i <= size; i++) {
sobelY = vpArray2D<FilterType>::conv2(sobelY, smoothingKernel, "full");
// Sobel(N+1)x(N+1) is the convolution of smoothingKernel, which needs 1/16 scale factor, with SobelNxN
scale *= 1./ 16.;
}

memcpy(filter, sobelY.data, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));

return 1 / 16.0;
return scale;
}

static float computeCannyThreshold(const vpImage<unsigned char> &I, float &lowerThresh,
Expand Down
5 changes: 3 additions & 2 deletions modules/core/src/image/vpCannyEdgeDetection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ vpCannyEdgeDetection::vpCannyEdgeDetection()
vpCannyEdgeDetection::vpCannyEdgeDetection(const int &gaussianKernelSize, const float &gaussianStdev
, const unsigned int &sobelAperture, const float &lowerThreshold, const float &upperThreshold
, const float &lowerThresholdRatio, const float &upperThresholdRatio
, const vpImageFilter::vpCannyFilteringAndGradientType &filteringType
)
: m_filteringAndGradientType(vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING)
: m_filteringAndGradientType(filteringType)
, m_gaussianKernelSize(gaussianKernelSize)
, m_gaussianStdev(gaussianStdev)
, m_areGradientAvailable(false)
Expand Down Expand Up @@ -109,7 +110,7 @@ vpCannyEdgeDetection::initGaussianFilters()
throw(vpException(vpException::badValue, "The Gaussian kernel size should be odd"));
}
m_fg.resize(1, (m_gaussianKernelSize + 1)/2);
vpImageFilter::getGaussianKernel(m_fg.data, m_gaussianKernelSize, m_gaussianStdev, false);
vpImageFilter::getGaussianKernel(m_fg.data, m_gaussianKernelSize, m_gaussianStdev, true);
}

void
Expand Down
40 changes: 27 additions & 13 deletions modules/core/src/image/vpImageFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,12 +631,16 @@ float vpImageFilter::computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p

// Compute the gradient of the blurred image
if (filteringType == vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING) {
cv::Sobel(img_blur, dIx, CV_16S, 1, 0, apertureGradient, 1, 0);
cv::Sobel(img_blur, dIy, CV_16S, 0, 1, apertureGradient, 1, 0);
double scale = 1. / 8.;
if (apertureGradient > 3) {
scale *= std::pow(1./16., ((apertureGradient -1.)/2.) - 1.);
}
cv::Sobel(img_blur, dIx, CV_16S, 1, 0, apertureGradient, 1, 0, scale);
cv::Sobel(img_blur, dIy, CV_16S, 0, 1, apertureGradient, 1, 0, scale);
}
else if (filteringType == vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING) {
cv::Scharr(img_blur, dIx, CV_16S, 1, 0);
cv::Scharr(img_blur, dIy, CV_16S, 0, 1);
cv::Scharr(img_blur, dIx, CV_16S, 1, 0, 1.f/32.f);
cv::Scharr(img_blur, dIy, CV_16S, 0, 1, 1.f/32.f);
}
}
else {
Expand Down Expand Up @@ -959,14 +963,18 @@ void vpImageFilter::canny(const vpImage<unsigned char> &Isrc, vpImage<unsigned c
if (cannyFilteringSteps == CANNY_GBLUR_SOBEL_FILTERING) {
cv::Mat cv_I_blur;
cv::GaussianBlur(img_cvmat, cv_I_blur, cv::Size((int)gaussianFilterSize, (int)gaussianFilterSize), gaussianStdev, 0);
cv::Sobel(cv_I_blur, cv_dx, CV_16S, 1, 0, apertureGradient);
cv::Sobel(cv_I_blur, cv_dy, CV_16S, 0, 1, apertureGradient);
double scale = 1. / 8.;
if (apertureGradient > 3) {
scale *= std::pow(1./16., ((apertureGradient -1.)/2.) - 1.);
}
cv::Sobel(cv_I_blur, cv_dx, CV_16S, 1, 0, apertureGradient, scale);
cv::Sobel(cv_I_blur, cv_dy, CV_16S, 0, 1, apertureGradient, scale);
}
else if (cannyFilteringSteps == CANNY_GBLUR_SCHARR_FILTERING) {
cv::Mat cv_I_blur;
cv::GaussianBlur(img_cvmat, cv_I_blur, cv::Size((int)gaussianFilterSize, (int)gaussianFilterSize), gaussianStdev, 0);
cv::Scharr(cv_I_blur, cv_dx, CV_16S, 1, 0);
cv::Scharr(cv_I_blur, cv_dy, CV_16S, 0, 1);
cv::Scharr(cv_I_blur, cv_dx, CV_16S, 1, 0, 1.f/32.f);
cv::Scharr(cv_I_blur, cv_dy, CV_16S, 0, 1, 1.f/32.f);
}
else {
std::string errMsg("[vpImageFilter::canny]Other types of Canny filtering steps have not been implemented");
Expand All @@ -976,7 +984,8 @@ void vpImageFilter::canny(const vpImage<unsigned char> &Isrc, vpImage<unsigned c
float lowerCannyThresh = lowerThreshold;
if (upperCannyThresh < 0) {
upperCannyThresh = computeCannyThreshold(img_cvmat, &cv_dx, &cv_dy, lowerCannyThresh, gaussianFilterSize,
gaussianStdev, apertureGradient, lowerThresholdRatio, upperThresholdRatio);
gaussianStdev, apertureGradient, lowerThresholdRatio, upperThresholdRatio,
cannyFilteringSteps);
}
else if (lowerCannyThresh < 0) {
lowerCannyThresh = upperCannyThresh / 3.f;
Expand All @@ -995,9 +1004,13 @@ void vpImageFilter::canny(const vpImage<unsigned char> &Isrc, vpImage<unsigned c
vpImage<float> dIx, dIy;
if (cannyFilteringSteps == CANNY_GBLUR_SOBEL_FILTERING
|| cannyFilteringSteps == CANNY_GBLUR_SCHARR_FILTERING) {
// Computing the Gaussian blur + gradients of the image
// Computing the Gaussian blur
vpImage<float> Iblur;
vpImageFilter::gaussianBlur(Isrc, Iblur, gaussianFilterSize, gaussianStdev);
vpArray2D<float> fg(1, (gaussianFilterSize + 1)/2);
vpImageFilter::getGaussianKernel(fg.data, gaussianFilterSize, gaussianStdev, true);
vpImage<float> GIx;
vpImageFilter::filterX<unsigned char, float>(Isrc, GIx, fg.data, gaussianFilterSize);
vpImageFilter::filterY<float, float>(GIx, Iblur, fg.data, gaussianFilterSize);

// Compute the gradient filters
vpArray2D<float> gradientFilterX(apertureGradient, apertureGradient); // Gradient filter along the X-axis
Expand Down Expand Up @@ -1040,13 +1053,14 @@ void vpImageFilter::canny(const vpImage<unsigned char> &Isrc, vpImage<unsigned c

if (upperCannyThresh < 0) {
upperCannyThresh = computeCannyThreshold(Isrc, lowerCannyThresh, &dIx, &dIy, gaussianFilterSize, gaussianStdev,
apertureGradient, lowerThresholdRatio, upperThresholdRatio);
apertureGradient, lowerThresholdRatio, upperThresholdRatio,
cannyFilteringSteps);
}
else if (lowerCannyThresh < 0) {
lowerCannyThresh = upperCannyThresh / 3.;
}
vpCannyEdgeDetection edgeDetector(gaussianFilterSize, gaussianStdev, apertureGradient, lowerCannyThresh, upperCannyThresh,
lowerThresholdRatio, upperThresholdRatio);
lowerThresholdRatio, upperThresholdRatio, cannyFilteringSteps);
edgeDetector.setGradients(dIx, dIy);
Ires = edgeDetector.detect(Isrc);
}
Expand Down
2 changes: 1 addition & 1 deletion modules/imgproc/src/vpCircleHoughTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ void
vpCircleHoughTransform::initGaussianFilters()
{
m_fg.resize(1, (m_algoParams.m_gaussianKernelSize + 1)/2);
vpImageFilter::getGaussianKernel(m_fg.data, m_algoParams.m_gaussianKernelSize, m_algoParams.m_gaussianStdev, false);
vpImageFilter::getGaussianKernel(m_fg.data, m_algoParams.m_gaussianKernelSize, m_algoParams.m_gaussianStdev, true);
m_cannyVisp.setGaussianFilterParameters(m_algoParams.m_gaussianKernelSize, m_algoParams.m_gaussianStdev);
}

Expand Down

0 comments on commit 40de54c

Please sign in to comment.