Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canny lower threshold #1231

Merged
merged 2 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions modules/core/include/visp3/core/vpImageFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class VISP_EXPORT vpImageFilter
static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, unsigned int gaussianFilterSize,
float thresholdCanny, unsigned int apertureSobel);

static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, unsigned int gaussianFilterSize,
float lowerThresholdCanny, float higherThresholdCanny, unsigned int apertureSobel);

/*!
Apply a 1x3 derivative filter to an image pixel.

Expand Down
71 changes: 65 additions & 6 deletions modules/core/src/image/vpImageFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,24 +297,83 @@ int main()
*/
void vpImageFilter::canny(const vpImage<unsigned char> &Isrc, vpImage<unsigned char> &Ires,
unsigned int gaussianFilterSize, float thresholdCanny, unsigned int apertureSobel)
{
vpImageFilter::canny(Isrc, Ires, gaussianFilterSize, thresholdCanny / 3.f, thresholdCanny, apertureSobel);
}

/*!
Apply the Canny edge operator on the image \e Isrc and return the resulting
image \e Ires.

The following example shows how to use the method:

\code
#include <visp3/core/vpImage.h>
#include <visp3/core/vpImageFilter.h>

int main()
{
// Constants for the Canny operator.
const unsigned int gaussianFilterSize = 5;
const float upperThresholdCanny = 15;
const float lowerThresholdCanny = 5;
const unsigned int apertureSobel = 3;

// Image for the Canny edge operator
vpImage<unsigned char> Isrc;
vpImage<unsigned char> Icanny;

// First grab the source image Isrc.

// Apply the Canny edge operator and set the Icanny image.
vpImageFilter::canny(Isrc, Icanny, gaussianFilterSize, lowerThresholdCanny, upperThresholdCanny, apertureSobel);
return (0);
}
\endcode

\param Isrc : Image to apply the Canny edge detector to.
\param Ires : Filtered image (255 means an edge, 0 otherwise).
\param gaussianFilterSize : The size of the mask of the Gaussian filter to
apply (an odd number).
\param lowerThreshold : The lower threshold for the Canny operator. Values lower
than this value are rejected. If negative, it will be set to one third
of the thresholdCanny .
\param upperThreshold : The upper threshold for the Canny operator. Only value
greater than this value are marked as an edge. If negative, it will be automatically
computed, along with the lower threshold. Otherwise, the lower threshold will be set to one third
of the thresholdCanny .
\param apertureSobel : Size of the mask for the Sobel operator (odd number).
*/
void vpImageFilter::canny(const vpImage<unsigned char> &Isrc, vpImage<unsigned char> &Ires,
unsigned int gaussianFilterSize, float lowerThreshold, float upperThreshold, unsigned int apertureSobel)
{
#if defined(HAVE_OPENCV_IMGPROC)
cv::Mat img_cvmat, cv_I_blur, edges_cvmat;
cv::Mat img_cvmat, cv_I_blur, cv_dx, cv_dy, edges_cvmat;
vpImageConvert::convert(Isrc, img_cvmat);
cv::GaussianBlur(img_cvmat, cv_I_blur, cv::Size((int)gaussianFilterSize, (int)gaussianFilterSize), 0, 0);
float upperCannyThresh = thresholdCanny;
float lowerCannyThresh = thresholdCanny / 3.f;
cv::Sobel(cv_I_blur, cv_dx, CV_16S, 1, 0, apertureSobel);
cv::Sobel(cv_I_blur, cv_dy, CV_16S, 0, 1, apertureSobel);
float upperCannyThresh = upperThreshold;
float lowerCannyThresh = lowerThreshold;
if (upperCannyThresh < 0) {
upperCannyThresh = computeCannyThreshold(img_cvmat, &cv_I_blur, lowerCannyThresh);
}
cv::Canny(cv_I_blur, edges_cvmat, lowerCannyThresh, upperCannyThresh, (int)apertureSobel);
else if (lowerCannyThresh < 0) {
lowerCannyThresh = upperCannyThresh / 3.f;
}
cv::Canny(cv_dx, cv_dy, edges_cvmat, lowerCannyThresh, upperCannyThresh, false);
vpImageConvert::convert(edges_cvmat, Ires);
#else
(void)apertureSobel;
if (thresholdCanny < 0) {
float upperCannyThresh = upperThreshold;
float lowerCannyThresh = lowerThreshold;
if (upperCannyThresh < 0) {
throw(vpException(vpException::badValue, "OpenCV imgproc module missing to be able to compute automatically the Canny thresholds"));
}
vpCannyEdgeDetection edgeDetector(gaussianFilterSize, 0.1, thresholdCanny * 0.5, thresholdCanny);
else if (lowerCannyThresh < 0) {
lowerCannyThresh = upperCannyThresh / 3.;
}
vpCannyEdgeDetection edgeDetector(gaussianFilterSize, 0.1, lowerCannyThresh, upperCannyThresh);
Ires = edgeDetector.detect(Isrc);
#endif
}
Expand Down
40 changes: 26 additions & 14 deletions modules/imgproc/include/visp3/imgproc/vpCircleHoughTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ class VISP_EXPORT vpCircleHoughTransform
int m_sobelKernelSize; /*!< Size of the Sobel kernels used to compute the gradients. Must be an odd number.*/

// // Edge detection attributes
float m_cannyThresh; /*!< The threshold for the Canny operator. Only value greater than this value are marked as an edge.
A negative value makes the algorithm compute this threshold automatically.*/
float m_lowerCannyThresh; /*!< The lower threshold for the Canny operator. Values lower than this value are rejected.
A negative value makes the algorithm compute the lower threshold automatically.*/
float m_upperCannyThresh; /*!< The upper threshold for the Canny operator. Only values greater than this value are marked as an edge.
A negative value makes the algorithm compute the upper and lower thresholds automatically.*/
int m_edgeMapFilteringNbIter; /*!< Number of iterations of 8-neighbor connectivity filtering to apply to the edge map*/

// // Center candidates computation attributes
Expand All @@ -107,7 +109,8 @@ class VISP_EXPORT vpCircleHoughTransform
: m_gaussianKernelSize(5)
, m_gaussianStdev(1.f)
, m_sobelKernelSize(3)
, m_cannyThresh(-1.f)
, m_lowerCannyThresh(-1.f)
, m_upperCannyThresh(-1.f)
, m_edgeMapFilteringNbIter(1)
, m_centerXlimits(std::pair<int, int>(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()))
, m_centerYlimits(std::pair<int, int>(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()))
Expand All @@ -129,8 +132,10 @@ class VISP_EXPORT vpCircleHoughTransform
* \param[in] gaussianKernelSize Size of the Gaussian filter kernel used to smooth the input image. Must be an odd number.
* \param[in] gaussianStdev Standard deviation of the Gaussian filter.
* \param[in] sobelKernelSize Size of the Sobel kernels used to compute the gradients. Must be an odd number.
* \param[in] cannyThresh The threshold for the Canny operator. Only value greater than this value are marked as an edge.
A negative value makes the algorithm compute this threshold automatically.
* \param[in] lowerCannyThresh The lower threshold for the Canny operator. Values lower than this value are rejected.
A negative value makes the algorithm compute this threshold and the lower one automatically.
* \param[in] upperCannyThresh The upper threshold for the Canny operator. Only values greater than this value are marked as an edge.
A negative value makes the algorithm compute this threshold and the lower one automatically.
* \param[in] edgeMapFilterNbIter Number of 8-neighbor connectivity filtering iterations to apply to the edge map.
* \param[in] centerXlimits Minimum and maximum position on the horizontal axis of the center of the circle we want to detect.
* \param[in] centerYlimits Minimum and maximum position on the vertical axis of the center of the circle we want to detect.
Expand All @@ -147,7 +152,8 @@ class VISP_EXPORT vpCircleHoughTransform
const int &gaussianKernelSize
, const float &gaussianStdev
, const int &sobelKernelSize
, const float &cannyThresh
, const float &lowerCannyThresh
, const float &upperCannyThresh
, const int &edgeMapFilterNbIter
, const std::pair<int, int> &centerXlimits
, const std::pair<int, int> &centerYlimits
Expand All @@ -163,7 +169,8 @@ class VISP_EXPORT vpCircleHoughTransform
: m_gaussianKernelSize(gaussianKernelSize)
, m_gaussianStdev(gaussianStdev)
, m_sobelKernelSize(sobelKernelSize)
, m_cannyThresh(cannyThresh)
, m_lowerCannyThresh(lowerCannyThresh)
, m_upperCannyThresh(upperCannyThresh)
, m_edgeMapFilteringNbIter(edgeMapFilterNbIter)
, m_centerXlimits(centerXlimits)
, m_centerYlimits(centerYlimits)
Expand All @@ -185,7 +192,7 @@ class VISP_EXPORT vpCircleHoughTransform
txt += "\tGaussian filter kernel size = " + std::to_string(m_gaussianKernelSize) + "\n";
txt += "\tGaussian filter standard deviation = " + std::to_string(m_gaussianStdev) + "\n";
txt += "\tSobel filter kernel size = " + std::to_string(m_sobelKernelSize) + "\n";
txt += "\tCanny edge filter threshold = " + std::to_string(m_cannyThresh) + "\n";
txt += "\tCanny edge filter thresholds = [" + std::to_string(m_lowerCannyThresh) + " ; " + std::to_string(m_upperCannyThresh) + "]\n";
txt += "\tEdge map 8-neighbor connectivity filtering number of iterations = " + std::to_string(m_edgeMapFilteringNbIter) + "\n";
txt += "\tCenter horizontal position limits: min = " + std::to_string(m_centerXlimits.first) + "\tmax = " + std::to_string(m_centerXlimits.second) +"\n";
txt += "\tCenter vertical position limits: min = " + std::to_string(m_centerYlimits.first) + "\tmax = " + std::to_string(m_centerYlimits.second) +"\n";
Expand Down Expand Up @@ -271,7 +278,8 @@ class VISP_EXPORT vpCircleHoughTransform
throw vpException(vpException::badValue, "Sobel Kernel size should be odd.");
}

params.m_cannyThresh = j.value("cannyThresh", params.m_cannyThresh);
params.m_lowerCannyThresh = j.value("lowerCannyThresh", params.m_lowerCannyThresh);
params.m_upperCannyThresh = j.value("upperCannyThresh", params.m_upperCannyThresh);
params.m_edgeMapFilteringNbIter = j.value("edgeMapFilteringNbIter", params.m_edgeMapFilteringNbIter);

params.m_centerXlimits = j.value("centerXlimits", params.m_centerXlimits);
Expand Down Expand Up @@ -320,7 +328,8 @@ class VISP_EXPORT vpCircleHoughTransform
{"gaussianKernelSize", params.m_gaussianKernelSize},
{"gaussianStdev", params.m_gaussianStdev},
{"sobelKernelSize", params.m_sobelKernelSize},
{"cannyThresh", params.m_cannyThresh},
{"lowerCannyThresh", params.m_lowerCannyThresh},
{"upperCannyThresh", params.m_upperCannyThresh},
{"edgeMapFilteringNbIter", params.m_edgeMapFilteringNbIter},
{"centerXlimits", params.m_centerXlimits},
{"centerYlimits", params.m_centerYlimits},
Expand Down Expand Up @@ -482,12 +491,15 @@ class VISP_EXPORT vpCircleHoughTransform
* Set the threshold for the Canny operator.
* Only value greater than this value are marked as an edge.
* If negative, the threshold is automatically computed.
* \param[in] canny_threshold : Canny filter upper threshold. When set to -1 (default), compute
* \param[in] lowerCannyThreshold : Canny filter lower threshold. When set to -1 (default), compute
* automatically this threshold.
* \param[in] upperCannyThreshold : Canny filter upper threshold. When set to -1 (default), compute
* automatically this threshold.
*/
inline void setCannyThreshold(const float &canny_threshold)
inline void setCannyThreshold(const float &lowerCannyThreshold, const float &upperCannyThreshold)
{
m_algoParams.m_cannyThresh = canny_threshold;
m_algoParams.m_lowerCannyThresh = lowerCannyThreshold;
m_algoParams.m_upperCannyThresh = upperCannyThreshold;
}

/*!
Expand Down Expand Up @@ -680,7 +692,7 @@ class VISP_EXPORT vpCircleHoughTransform
*/
inline float getCannyThreshold() const
{
return m_algoParams.m_cannyThresh;
return m_algoParams.m_upperCannyThresh;
}

/*!
Expand Down
15 changes: 9 additions & 6 deletions modules/imgproc/src/vpCircleHoughTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,16 +220,19 @@ void
vpCircleHoughTransform::edgeDetection(const vpImage<unsigned char> &I)
{
#if defined(HAVE_OPENCV_IMGPROC)
float cannyThresh = m_algoParams.m_cannyThresh;
float lowerThresh;
float upperCannyThresh = m_algoParams.m_upperCannyThresh;
float lowerCannyThresh = m_algoParams.m_lowerCannyThresh;
// Apply the Canny edge operator to compute the edge map
// The canny method performs Gaussian blur and gradient computation
if (m_algoParams.m_cannyThresh < 0.) {
cannyThresh = vpImageFilter::computeCannyThreshold(I, lowerThresh);
if (m_algoParams.m_upperCannyThresh < 0.) {
upperCannyThresh = vpImageFilter::computeCannyThreshold(I, lowerCannyThresh);
}
vpImageFilter::canny(I, m_edgeMap, m_algoParams.m_gaussianKernelSize, cannyThresh, m_algoParams.m_sobelKernelSize);
else if (m_algoParams.m_lowerCannyThresh < 0) {
lowerCannyThresh = upperCannyThresh / 3.;
}
vpImageFilter::canny(I, m_edgeMap, m_algoParams.m_gaussianKernelSize, lowerCannyThresh, upperCannyThresh, m_algoParams.m_sobelKernelSize);
#else
m_cannyVisp.setCannyThresholds(-1, m_algoParams.m_cannyThresh);
m_cannyVisp.setCannyThresholds(m_algoParams.m_lowerCannyThresh, m_algoParams.m_upperCannyThresh);
m_cannyVisp.setGradients(m_dIx, m_dIy);
m_edgeMap = m_cannyVisp.detect(I);
#endif
Expand Down
3 changes: 2 additions & 1 deletion tutorial/imgproc/hough-transform/config/detector_full.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cannyThresh": 150.0,
"lowerCannyThresh": 50.0,
"upperCannyThresh": 150.0,
"centerMinDistance": 15.0,
"centerThresh": 100.0,
"centerXlimits": [
Expand Down
3 changes: 2 additions & 1 deletion tutorial/imgproc/hough-transform/config/detector_half.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cannyThresh": 150.0,
"lowerCannyThresh": 50.0,
"upperCannyThresh": 150.0,
"centerMinDistance": 15.0,
"centerThresh": 50.0,
"centerXlimits": [
Expand Down
3 changes: 2 additions & 1 deletion tutorial/imgproc/hough-transform/config/detector_img.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cannyThresh": -1.0,
"lowerCannyThresh": -1.0,
"upperCannyThresh": -1.0,
"centerMinDistance": 5.0,
"centerThresh": 100.0,
"centerXlimits": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cannyThresh": 150.0,
"lowerCannyThresh": 50.0,
"upperCannyThresh": 150.0,
"centerMinDistance": 15.0,
"centerThresh": 25.0,
"centerXlimits": [
Expand Down
27 changes: 16 additions & 11 deletions tutorial/imgproc/hough-transform/tutorial-circle-hough.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,11 @@ int main(int argc, char **argv)
const float def_gaussianSigma = 1.f;
const int def_sobelKernelSize = 3;
#ifdef HAVE_OPENCV_IMGPROC
const float def_cannyThresh = 150.f;
const float def_lowerCannyThresh = 50.f;
const float def_upperCannyThresh = 150.f;
#else
const float def_cannyThresh = 25.f;
const float def_lowerCannyThresh = 8.f;
const float def_upperCannyThresh = 25.f;
#endif
const int def_nbEdgeFilteringIter = 2;
const std::pair<int, int> def_centerXlimits = std::pair<int, int>(0, 640);
Expand All @@ -221,7 +223,8 @@ int main(int argc, char **argv)
int opt_gaussianKernelSize = def_gaussianKernelSize;
float opt_gaussianSigma = def_gaussianSigma;
int opt_sobelKernelSize = def_sobelKernelSize;
float opt_cannyThresh = def_cannyThresh;
float opt_lowerCannyThresh = def_lowerCannyThresh;
float opt_upperCannyThresh = def_upperCannyThresh;
int opt_nbEdgeFilteringIter = def_nbEdgeFilteringIter;
std::pair<int, int> opt_centerXlimits = def_centerXlimits;
std::pair<int, int> opt_centerYlimits = def_centerYlimits;
Expand Down Expand Up @@ -263,9 +266,10 @@ int main(int argc, char **argv)
opt_sobelKernelSize = atoi(argv[i + 1]);
i++;
}
else if (argName == "--canny-thresh" && i + 1 < argc) {
opt_cannyThresh = static_cast<float>(atof(argv[i + 1]));
i++;
else if (argName == "--canny-thresh" && i + 2 < argc) {
opt_lowerCannyThresh = static_cast<float>(atof(argv[i + 1]));
opt_upperCannyThresh = static_cast<float>(atof(argv[i + 2]));
i += 2;
}
else if (argName == "--edge-filter" && i + 1 < argc) {
opt_nbEdgeFilteringIter = atoi(argv[i + 1]);
Expand Down Expand Up @@ -322,7 +326,7 @@ int main(int argc, char **argv)
<< "\t [--gaussian-kernel <kernel-size>] (default: " << def_gaussianKernelSize << ")" << std::endl
<< "\t [--gaussian-sigma <stddev>] (default: " << def_gaussianSigma << ")" << std::endl
<< "\t [--sobel-kernel <kernel-size>] (default: " << def_sobelKernelSize << ")" << std::endl
<< "\t [--canny-thresh <canny-thresh>] (default: " << def_cannyThresh << ")" << std::endl
<< "\t [--canny-thresh <lower-canny-thresh upper-canny-thresh>] (default: " << def_lowerCannyThresh << " ; " << def_upperCannyThresh << ")" << std::endl
<< "\t [--edge-filter <nb-iter>] (default: " << def_nbEdgeFilteringIter << ")" << std::endl
<< "\t [--radius-limits <radius-min> <radius-max>] (default: min = " << def_minRadius << ", max = " << def_maxRadius << ")" << std::endl
<< "\t [--dilatation-repet <nb-repetitions>] (default: " << def_dilatationRepet << ")" << std::endl
Expand Down Expand Up @@ -363,9 +367,9 @@ int main(int argc, char **argv)
<< "\t\tDefault: " << def_gaussianSigma << std::endl
<< std::endl
<< "\t--canny-thresh" << std::endl
<< "\t\tPermit to set the upper threshold of the Canny edge detector." << std::endl
<< "\t\tMust be a positive value." << std::endl
<< "\t\tDefault: " << def_cannyThresh << std::endl
<< "\t\tPermit to set the lower and upper thresholds of the Canny edge detector." << std::endl
<< "\t\tIf a value is negative, it will be automatically computed." << std::endl
<< "\t\tDefault: " << def_upperCannyThresh << std::endl
<< std::endl
<< "\t--edge-filter" << std::endl
<< "\t\tPermit to set the number of iteration of 8-neighbor filter iterations of the result of the Canny edge detector." << std::endl
Expand Down Expand Up @@ -478,7 +482,8 @@ int main(int argc, char **argv)
algoParams(opt_gaussianKernelSize
, opt_gaussianSigma
, opt_sobelKernelSize
, opt_cannyThresh
, opt_lowerCannyThresh
, opt_upperCannyThresh
, opt_nbEdgeFilteringIter
, opt_centerXlimits
, opt_centerYlimits
Expand Down