Skip to content

Commit

Permalink
[CORPS] Added Scharr 3x3 filter and CANNY_GBLUR_SCHARR_FILTERING option
Browse files Browse the repository at this point in the history
  • Loading branch information
rlagneau committed Oct 18, 2023
1 parent d916053 commit 3692eb4
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 114 deletions.
28 changes: 13 additions & 15 deletions modules/core/include/visp3/core/vpCannyEdgeDetection.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ class VISP_EXPORT vpCannyEdgeDetection

// // Gradient computation attributes
bool m_areGradientAvailable; /*!< Set to true if the user provides the gradient images, false otherwise. In the latter case, the class will compute the gradients.*/
unsigned int m_sobelAperture; /*!< The size of the Sobel kernels used to compute the gradients of the image.*/
vpArray2D<float> m_sobelX; /*!< Array that contains the Sobel kernel along the X-axis.*/
vpArray2D<float> m_sobelY; /*!< Array that contains the Sobel kernel along the Y-axis.*/
unsigned int m_gradientFilterKernelSize; /*!< The size of the Sobel kernels used to compute the gradients of the image.*/
vpArray2D<float> m_gradientFilterX; /*!< Array that contains the gradient filter kernel (Sobel or Scharr) along the X-axis.*/
vpArray2D<float> m_gradientFilterY; /*!< Array that contains the gradient filter kernel (Sobel or Scharr) along the Y-axis.*/
vpImage<float> m_dIx; /*!< X-axis gradient.*/
vpImage<float> m_dIy; /*!< Y-axis gradient.*/

Expand Down Expand Up @@ -98,9 +98,9 @@ class VISP_EXPORT vpCannyEdgeDetection
void initGaussianFilters();

/**
* \brief Initialize the Sobel filters used to compute the input image gradients.
* \brief Initialize the gradient filters (Sobel or Scharr) used to compute the input image gradients.
*/
void initSobelFilters();
void initGradientFilters();
//@}

/** @name Different steps methods */
Expand Down Expand Up @@ -216,7 +216,7 @@ class VISP_EXPORT vpCannyEdgeDetection
detector.m_gaussianStdev = j.value("gaussianStdev", detector.m_gaussianStdev);
detector.m_lowerThreshold = j.value("lowerThreshold", detector.m_lowerThreshold);
detector.m_lowerThresholdRatio = j.value("lowerThresholdRatio", detector.m_lowerThresholdRatio);
detector.m_sobelAperture = j.value("sobelAperture", detector.m_sobelAperture);
detector.m_gradientFilterKernelSize = j.value("gradientFilterKernelSize", detector.m_gradientFilterKernelSize);
detector.m_upperThreshold = j.value("upperThreshold", detector.m_upperThreshold);
detector.m_upperThresholdRatio = j.value("upperThresholdRatio", detector.m_upperThresholdRatio);
}
Expand All @@ -236,7 +236,7 @@ class VISP_EXPORT vpCannyEdgeDetection
{"gaussianStdev", detector.m_gaussianStdev},
{"lowerThreshold", detector.m_lowerThreshold},
{"lowerThresholdRatio", detector.m_lowerThresholdRatio},
{"sobelAperture", detector.m_sobelAperture},
{"gradientFilterKernelSize", detector.m_gradientFilterKernelSize},
{"upperThreshold", detector.m_upperThreshold},
{"upperThresholdRatio", detector.m_upperThresholdRatio}
};
Expand Down Expand Up @@ -285,6 +285,7 @@ class VISP_EXPORT vpCannyEdgeDetection
inline void setFilteringAndGradientType(const vpImageFilter::vpCannyFilteringAndGradientType &type)
{
m_filteringAndGradientType = type;
initGradientFilters();
}

/**
Expand Down Expand Up @@ -351,17 +352,14 @@ class VISP_EXPORT vpCannyEdgeDetection
}

/**
* \brief Set the Gaussian Filters kernel size and standard deviation
* and initialize the aforementioned filters.
* \brief Set the parameters of the gradient filter (Sobel or Scharr) kernel size filters.
*
* \param[in] kernelSize : The size of the Gaussian filters kernel.
* \param[in] stdev : The standard deviation of the Gaussian filters used to blur and
* compute the gradient of the image.
* \param[in] apertureSize The size of the gradient filters kernel. Must be an odd value.
*/
inline void setSobelAperture(const unsigned int &sobelAperture)
inline void setGradientFilterAperture(const unsigned int &apertureSize)
{
m_sobelAperture = sobelAperture;
initSobelFilters();
m_gradientFilterKernelSize = apertureSize;
initGradientFilters();
}
//@}
};
Expand Down
59 changes: 56 additions & 3 deletions modules/core/include/visp3/core/vpImageFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ class VISP_EXPORT vpImageFilter
typedef enum vpCannyFilteringAndGradientType
{
CANNY_GBLUR_SOBEL_FILTERING = 0, //!< Apply Gaussian blur + Sobel operator on the input image
CANNY_COUNT_FILTERING = 1 //! Number of supported backends
CANNY_GBLUR_SCHARR_FILTERING = 1, //!< Apply Gaussian blur + Scharr operator on the input image
CANNY_COUNT_FILTERING = 2 //! Number of supported backends
} vpCannyFilteringAndGradientType;

static std::string vpCannyFilteringAndGradientTypeToString(const vpCannyFilteringAndGradientType &type);
Expand Down Expand Up @@ -1007,6 +1008,56 @@ class VISP_EXPORT vpImageFilter
vpImageFilter::getGradY<FilterType, FilterType>(GIx, dIy, gaussianDerivativeKernel, size);
}

/*!
Get Scharr kernel for X-direction.
\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.
*/
template <typename FilterType>
inline static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
{
if (size != 1) {
// Size = 1 => kernel_size = 2*1 + 1 = 3
std::string errMsg = "Cannot get Scharr kernel of size " + std::to_string(size * 2 + 1) + " != 3";
throw vpException(vpException::dimensionError, errMsg);
}

vpArray2D<FilterType> ScharrY(size * 2 + 1, size * 2 + 1);
FilterType norm = getScharrKernelY<FilterType>(ScharrY.data, size);
memcpy(filter, ScharrY.t().data, ScharrY.getRows() * ScharrY.getCols() * sizeof(FilterType));
return norm;
}

/*!
Get Scharr kernel for Y-direction.
\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.
*/
template <typename FilterType>
inline static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
{
// Scharr kernel pre-computed for the usual size
static const FilterType ScharrY3x3[9] = { -3.0, -10.0, -3.0, 0.0, 0.0, 0.0, 3.0, 10.0, 3.0 };

if (size != 1) {
// Size = 1 => kernel_size = 2*1 + 1 = 3
std::string errMsg = "Cannot get Scharr kernel of size " + std::to_string(size * 2 + 1) + " != 3";
throw vpException(vpException::dimensionError, errMsg);
}

const unsigned int kernel_size = size * 2 + 1;
if (kernel_size == 3) {
memcpy(filter, ScharrY3x3, kernel_size * kernel_size * sizeof(FilterType));
return 1 / 32.0;
}

return 0.;
}

/*!
Get Sobel kernel for X-direction.
\tparam FilterType: Either float, to accelerate the computation time, or double, to have greater precision.
Expand Down Expand Up @@ -1090,13 +1141,15 @@ class VISP_EXPORT vpImageFilter
const vpImage<float> *p_dIx = nullptr, const vpImage<float> *p_dIy = nullptr,
const unsigned int gaussianKernelSize = 5,
const float gaussianStdev = 2.f, const unsigned int apertureSobel = 3,
const float lowerThresholdRatio = 0.6, const float upperThresholdRatio = 0.8);
const float lowerThresholdRatio = 0.6, const float upperThresholdRatio = 0.8,
const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);

#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
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 apertureSobel = 3,
const float lowerThresholdRatio = 0.6, const float upperThresholdRatio = 0.8);
const float lowerThresholdRatio = 0.6, const float upperThresholdRatio = 0.8,
const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
static float median(const cv::Mat &cv_I);
static float median(const vpImage<unsigned char> &Isrc);
static std::vector<float> median(const vpImage<vpRGBa> &Isrc);
Expand Down
41 changes: 24 additions & 17 deletions modules/core/src/image/vpCannyEdgeDetection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ vpCannyEdgeDetection::vpCannyEdgeDetection()
, m_gaussianKernelSize(3)
, m_gaussianStdev(1.f)
, m_areGradientAvailable(false)
, m_sobelAperture(3)
, m_gradientFilterKernelSize(3)
, m_lowerThreshold(-1.f)
, m_lowerThresholdRatio(0.6f)
, m_upperThreshold(-1.f)
, m_upperThresholdRatio(0.8f)
{
initGaussianFilters();
initSobelFilters();
initGradientFilters();
}

vpCannyEdgeDetection::vpCannyEdgeDetection(const int &gaussianKernelSize, const float &gaussianStdev
Expand All @@ -59,14 +59,14 @@ vpCannyEdgeDetection::vpCannyEdgeDetection(const int &gaussianKernelSize, const
, m_gaussianKernelSize(gaussianKernelSize)
, m_gaussianStdev(gaussianStdev)
, m_areGradientAvailable(false)
, m_sobelAperture(sobelAperture)
, m_gradientFilterKernelSize(sobelAperture)
, m_lowerThreshold(lowerThreshold)
, m_lowerThresholdRatio(lowerThresholdRatio)
, m_upperThreshold(upperThreshold)
, m_upperThresholdRatio(upperThresholdRatio)
{
initGaussianFilters();
initSobelFilters();
initGradientFilters();
}

#ifdef VISP_HAVE_NLOHMANN_JSON
Expand Down Expand Up @@ -95,10 +95,10 @@ vpCannyEdgeDetection::initFromJSON(const std::string &jsonPath)
msg << "Byte position of error: " << e.byte;
throw vpException(vpException::ioError, msg.str());
}
*this = j; // Call from_json(const json& j, vpDetectionCircle2D& *this) to read json
from_json(j, *this);
file.close();
initGaussianFilters();
initSobelFilters();
initGradientFilters();
}
#endif

Expand All @@ -113,16 +113,23 @@ vpCannyEdgeDetection::initGaussianFilters()
}

void
vpCannyEdgeDetection::initSobelFilters()
vpCannyEdgeDetection::initGradientFilters()
{
if ((m_sobelAperture % 2) == 0) {
std::string errMsg("The Sobel kernel (" + std::to_string(m_sobelAperture) + ") should be odd");
throw(vpException(vpException::badValue, errMsg));
if ((m_gradientFilterKernelSize % 2) != 1) {
throw vpException(vpException::badValue, "Gradient filters kernel size should be odd.");
}
m_gradientFilterX.resize(m_gradientFilterKernelSize, m_gradientFilterKernelSize);
m_gradientFilterY.resize(m_gradientFilterKernelSize, m_gradientFilterKernelSize);

if (m_filteringAndGradientType == vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING) {
vpImageFilter::getSobelKernelX(m_gradientFilterX.data, (m_gradientFilterKernelSize - 1)/2);
vpImageFilter::getSobelKernelY(m_gradientFilterY.data, (m_gradientFilterKernelSize - 1)/2);
}
else if (m_filteringAndGradientType == vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING) {
// Compute the Scharr filters
vpImageFilter::getScharrKernelX(m_gradientFilterX.data, (m_gradientFilterKernelSize - 1)/2);
vpImageFilter::getScharrKernelY(m_gradientFilterY.data, (m_gradientFilterKernelSize - 1)/2);
}
m_sobelX.resize(m_sobelAperture, m_sobelAperture);
vpImageFilter::getSobelKernelX(m_sobelX.data, (m_sobelAperture - 1)/2);
m_sobelY.resize(m_sobelAperture, m_sobelAperture);
vpImageFilter::getSobelKernelY(m_sobelY.data, (m_sobelAperture - 1)/2);
}

// // Detection methods
Expand Down Expand Up @@ -166,7 +173,7 @@ vpCannyEdgeDetection::detect(const vpImage<unsigned char> &I)
float lowerThreshold = m_lowerThreshold;
if (upperThreshold < 0) {
upperThreshold = vpImageFilter::computeCannyThreshold(I, lowerThreshold, &m_dIx, &m_dIy, m_gaussianKernelSize,
m_gaussianStdev, m_sobelAperture, m_lowerThresholdRatio,
m_gaussianStdev, m_gradientFilterKernelSize, m_lowerThresholdRatio,
m_upperThresholdRatio);
}
else if (m_lowerThreshold < 0) {
Expand All @@ -192,8 +199,8 @@ vpCannyEdgeDetection::performFilteringAndGradientComputation(const vpImage<unsig
vpImageFilter::filterY<float, float>(GIx, Iblur, m_fg.data, m_gaussianKernelSize);

// Computing the gradients
vpImageFilter::filter(Iblur, m_dIx, m_sobelX);
vpImageFilter::filter(Iblur, m_dIy, m_sobelY);
vpImageFilter::filter(Iblur, m_dIx, m_gradientFilterX);
vpImageFilter::filter(Iblur, m_dIy, m_gradientFilterY);
}
else {
std::string errmsg("Currently, the only filtering and gradient operators are Gaussian blur + Sobel");
Expand Down
Loading

0 comments on commit 3692eb4

Please sign in to comment.