From 89bb080108b8b0d896d45c0443c748ec1a05de75 Mon Sep 17 00:00:00 2001 From: rlagneau Date: Thu, 30 Nov 2023 15:39:46 +0100 Subject: [PATCH 1/4] [FIX] Fix Canny: problem came from the way the 'absolute theta' is computed --- modules/core/src/image/vpCannyEdgeDetection.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/core/src/image/vpCannyEdgeDetection.cpp b/modules/core/src/image/vpCannyEdgeDetection.cpp index 84f955f491..904fa8897f 100644 --- a/modules/core/src/image/vpCannyEdgeDetection.cpp +++ b/modules/core/src/image/vpCannyEdgeDetection.cpp @@ -314,13 +314,15 @@ getManhattanGradient(const vpImage &dIx, const vpImage &dIy, const } /** - * @brief Get the absolute value of the gradient orientation. + * @brief Get the gradient orientation, expressed in degrees, between 0 and +180 degrees. + * If the gradient orientation is negative, we add 180 degrees (i.e. M_PI radiants) in + * order to keep the same orientation but in the positive direction. * * @param dIx : Gradient along the horizontal axis. * @param dIy : Gradient along the vertical axis. * @param row : Index along the vertical axis. * @param col : Index along the horizontal axis. - * @return float The absolute value of the gradient orientation, expressed in degrees. + * @return float The positive value of the gradient orientation, expressed in degrees. */ float getAbsoluteTheta(const vpImage &dIx, const vpImage &dIy, const int &row, const int &col) @@ -333,7 +335,12 @@ getAbsoluteTheta(const vpImage &dIx, const vpImage &dIy, const int absoluteTheta = 90.f; } else { - absoluteTheta = static_cast(vpMath::deg(std::abs(std::atan2(dy, dx)))); + // -dy because the y-axis of the image is oriented towards the bottom of the screen + // while we later work with a y-axis oriented towards the top when getting the theta quadrant. + absoluteTheta = static_cast(vpMath::deg(std::atan2(-dy , dx))); + if(absoluteTheta < 0.f) { + absoluteTheta += 180.f; // + M_PI in order to be between 0 and M_PI + } } return absoluteTheta; } From ac4a09fc97105ee15d6fbdb3e634860b1326ad86 Mon Sep 17 00:00:00 2001 From: rlagneau Date: Thu, 30 Nov 2023 15:45:39 +0100 Subject: [PATCH 2/4] [FIX] Fix a mistake in the way partial derivatives computed by OpenCV are normalized --- modules/core/src/image/vpImageFilter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/core/src/image/vpImageFilter.cpp b/modules/core/src/image/vpImageFilter.cpp index 20f55874a9..629ee1e063 100644 --- a/modules/core/src/image/vpImageFilter.cpp +++ b/modules/core/src/image/vpImageFilter.cpp @@ -684,7 +684,7 @@ float vpImageFilter::computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p const float range[] = { 0.f, 256.f }; // The upper boundary is exclusive const float *ranges[] = { range }; int channels[] = { 0 }; - bool dims = 1; // The number of dimensions of the histogram + int dims = 1; // The number of dimensions of the histogram int histSize[] = { bins }; bool uniform = true; bool accumulate = false; // Clear the histogram at the beginning of calcHist if false, does not clear it otherwise @@ -741,14 +741,14 @@ void vpImageFilter::computePartialDerivatives(const cv::Mat &cv_I, if (normalize) { scale = 1. / 8.; if (apertureGradient > 3) { - scale *= std::pow(1./16., ((apertureGradient -1.)/2.) - 1.); + scale *= std::pow(1./2., (apertureGradient * 2. - 3.)); // 1 / 2^(2 x ksize - dx - dy -2) with ksize =apertureGradient and dx xor dy = 1 } } if (computeDx) { - cv::Sobel(img_blur, cv_dIx, CV_16S, 1, 0, apertureGradient, 1, 0, scale); + cv::Sobel(img_blur, cv_dIx, CV_16S, 1, 0, apertureGradient, scale, 0., cv::BORDER_REPLICATE); } if (computeDy) { - cv::Sobel(img_blur, cv_dIy, CV_16S, 0, 1, apertureGradient, 1, 0, scale); + cv::Sobel(img_blur, cv_dIy, CV_16S, 0, 1, apertureGradient, scale, 0., cv::BORDER_REPLICATE); } } else if (filteringType == vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING) { From 43f94419fe91defba8484e751a80688e9803ea5d Mon Sep 17 00:00:00 2001 From: rlagneau Date: Thu, 30 Nov 2023 16:18:28 +0100 Subject: [PATCH 3/4] [CLEAN] Renamed absoluteTheta for positiveTheta, to be less confusing --- .../core/src/image/vpCannyEdgeDetection.cpp | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/core/src/image/vpCannyEdgeDetection.cpp b/modules/core/src/image/vpCannyEdgeDetection.cpp index 904fa8897f..b2a48a4df5 100644 --- a/modules/core/src/image/vpCannyEdgeDetection.cpp +++ b/modules/core/src/image/vpCannyEdgeDetection.cpp @@ -235,7 +235,7 @@ vpCannyEdgeDetection::performFilteringAndGradientComputation(const vpImage= 45.f && absoluteTheta < 90.f) { + else if (positiveTheta >= 45.f && positiveTheta < 90.f) { // Angles between 45 and 90 deg rely on the diagonal and vertical points thetaMin = 45.f; dColGradAlpha = 1; @@ -266,7 +266,7 @@ getInterpolationWeightsAndOffsets(const float &absoluteTheta, dRowGradAlpha = -1; dRowGradBeta = -1; } - else if (absoluteTheta >= 90.f && absoluteTheta < 135.f) { + else if (positiveTheta >= 90.f && positiveTheta < 135.f) { // Angles between 90 and 135 deg rely on the vertical and diagonal points thetaMin = 90.f; dColGradAlpha = 0; @@ -274,7 +274,7 @@ getInterpolationWeightsAndOffsets(const float &absoluteTheta, dRowGradAlpha = -1; dRowGradBeta = -1; } - else if (absoluteTheta >= 135.f && absoluteTheta < 180.f) { + else if (positiveTheta >= 135.f && positiveTheta < 180.f) { // Angles between 135 and 180 deg rely on the vertical and diagonal points thetaMin = 135.f; dColGradAlpha = -1; @@ -282,7 +282,7 @@ getInterpolationWeightsAndOffsets(const float &absoluteTheta, dRowGradAlpha = -1; dRowGradBeta = 0; } - beta = (absoluteTheta - thetaMin) / 45.f; + beta = (positiveTheta - thetaMin) / 45.f; alpha = 1.f - beta; } @@ -325,24 +325,24 @@ getManhattanGradient(const vpImage &dIx, const vpImage &dIy, const * @return float The positive value of the gradient orientation, expressed in degrees. */ float -getAbsoluteTheta(const vpImage &dIx, const vpImage &dIy, const int &row, const int &col) +getPositiveTheta(const vpImage &dIx, const vpImage &dIy, const int &row, const int &col) { - float absoluteTheta = 0.f; + float positiveTheta = 0.f; float dx = dIx[row][col]; float dy = dIy[row][col]; if (std::abs(dx) < std::numeric_limits::epsilon()) { - absoluteTheta = 90.f; + positiveTheta = 90.f; } else { // -dy because the y-axis of the image is oriented towards the bottom of the screen // while we later work with a y-axis oriented towards the top when getting the theta quadrant. - absoluteTheta = static_cast(vpMath::deg(std::atan2(-dy , dx))); - if(absoluteTheta < 0.f) { - absoluteTheta += 180.f; // + M_PI in order to be between 0 and M_PI + positiveTheta = static_cast(vpMath::deg(std::atan2(-dy , dx))); + if(positiveTheta < 0.f) { + positiveTheta += 180.f; // + M_PI in order to be between 0 and M_PI } } - return absoluteTheta; + return positiveTheta; } void @@ -365,9 +365,9 @@ vpCannyEdgeDetection::performEdgeThinning(const float &lowerThreshold) // depending on the gradient orientation int dRowAlphaPlus = 0, dRowBetaPlus = 0; int dColAphaPlus = 0, dColBetaPlus = 0; - float absTheta = getAbsoluteTheta(m_dIx, m_dIy, row, col); + float positiveTheta = getPositiveTheta(m_dIx, m_dIy, row, col); float alpha = 0.f, beta = 0.f; - getInterpolationWeightsAndOffsets(absTheta, alpha, beta, dRowAlphaPlus, dRowBetaPlus, dColAphaPlus, dColBetaPlus); + getInterpolationWeightsAndOffsets(positiveTheta, alpha, beta, dRowAlphaPlus, dRowBetaPlus, dColAphaPlus, dColBetaPlus); int dRowAlphaMinus = -dRowAlphaPlus, dRowBetaMinus = -dRowBetaPlus; int dColAphaMinus = -dColAphaPlus, dColBetaMinus = -dColBetaPlus; float gradAlphaPlus = getManhattanGradient(m_dIx, m_dIy, row + dRowAlphaPlus, col + dColAphaPlus); From e459437c747354287392d4a6761cb5393aaea853 Mon Sep 17 00:00:00 2001 From: rlagneau Date: Fri, 1 Dec 2023 11:17:59 +0100 Subject: [PATCH 4/4] [CORPS] Working with radians instead of degrees [CLEAN] Changed positiveGradient for gradientOrientation that is more understandable --- .../core/src/image/vpCannyEdgeDetection.cpp | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/modules/core/src/image/vpCannyEdgeDetection.cpp b/modules/core/src/image/vpCannyEdgeDetection.cpp index b2a48a4df5..884f2f02f5 100644 --- a/modules/core/src/image/vpCannyEdgeDetection.cpp +++ b/modules/core/src/image/vpCannyEdgeDetection.cpp @@ -235,7 +235,8 @@ vpCannyEdgeDetection::performFilteringAndGradientComputation(const vpImage= 45.f && positiveTheta < 90.f) { + else if (gradientOrientation >= M_PI_4f && gradientOrientation < M_PI_2f) { // Angles between 45 and 90 deg rely on the diagonal and vertical points - thetaMin = 45.f; + thetaMin = M_PI_4f; dColGradAlpha = 1; dColGradBeta = 0; dRowGradAlpha = -1; dRowGradBeta = -1; } - else if (positiveTheta >= 90.f && positiveTheta < 135.f) { + else if (gradientOrientation >= M_PI_2f && gradientOrientation < (3.f * M_PI_4f)) { // Angles between 90 and 135 deg rely on the vertical and diagonal points - thetaMin = 90.f; + thetaMin = M_PI_2f; dColGradAlpha = 0; dColGradBeta = -1; dRowGradAlpha = -1; dRowGradBeta = -1; } - else if (positiveTheta >= 135.f && positiveTheta < 180.f) { + else if (gradientOrientation >= (3.f * M_PI_4f) && gradientOrientation < M_PIf) { // Angles between 135 and 180 deg rely on the vertical and diagonal points - thetaMin = 135.f; + thetaMin = 3.f * M_PI_4f; dColGradAlpha = -1; dColGradBeta = -1; dRowGradAlpha = -1; dRowGradBeta = 0; } - beta = (positiveTheta - thetaMin) / 45.f; + beta = (gradientOrientation - thetaMin) / M_PI_4f; alpha = 1.f - beta; } @@ -314,35 +315,35 @@ getManhattanGradient(const vpImage &dIx, const vpImage &dIy, const } /** - * @brief Get the gradient orientation, expressed in degrees, between 0 and +180 degrees. - * If the gradient orientation is negative, we add 180 degrees (i.e. M_PI radiants) in + * @brief Get the gradient orientation, expressed in radians, between 0 and M_PIf radians. + * If the gradient orientation is negative, we add M_PI radians in * order to keep the same orientation but in the positive direction. * * @param dIx : Gradient along the horizontal axis. * @param dIy : Gradient along the vertical axis. * @param row : Index along the vertical axis. * @param col : Index along the horizontal axis. - * @return float The positive value of the gradient orientation, expressed in degrees. + * @return float The positive value of the gradient orientation, expressed in radians. */ float -getPositiveTheta(const vpImage &dIx, const vpImage &dIy, const int &row, const int &col) +getGradientOrientation(const vpImage &dIx, const vpImage &dIy, const int &row, const int &col) { - float positiveTheta = 0.f; + float gradientOrientation = 0.f; float dx = dIx[row][col]; float dy = dIy[row][col]; if (std::abs(dx) < std::numeric_limits::epsilon()) { - positiveTheta = 90.f; + gradientOrientation = M_PI_2f; } else { // -dy because the y-axis of the image is oriented towards the bottom of the screen // while we later work with a y-axis oriented towards the top when getting the theta quadrant. - positiveTheta = static_cast(vpMath::deg(std::atan2(-dy , dx))); - if(positiveTheta < 0.f) { - positiveTheta += 180.f; // + M_PI in order to be between 0 and M_PI + gradientOrientation = static_cast(std::atan2(-dy , dx)); + if(gradientOrientation < 0.f) { + gradientOrientation += M_PIf; // + M_PI in order to be between 0 and M_PIf } } - return positiveTheta; + return gradientOrientation; } void @@ -365,9 +366,9 @@ vpCannyEdgeDetection::performEdgeThinning(const float &lowerThreshold) // depending on the gradient orientation int dRowAlphaPlus = 0, dRowBetaPlus = 0; int dColAphaPlus = 0, dColBetaPlus = 0; - float positiveTheta = getPositiveTheta(m_dIx, m_dIy, row, col); + float gradientOrientation = getGradientOrientation(m_dIx, m_dIy, row, col); float alpha = 0.f, beta = 0.f; - getInterpolationWeightsAndOffsets(positiveTheta, alpha, beta, dRowAlphaPlus, dRowBetaPlus, dColAphaPlus, dColBetaPlus); + getInterpolationWeightsAndOffsets(gradientOrientation, alpha, beta, dRowAlphaPlus, dRowBetaPlus, dColAphaPlus, dColBetaPlus); int dRowAlphaMinus = -dRowAlphaPlus, dRowBetaMinus = -dRowBetaPlus; int dColAphaMinus = -dColAphaPlus, dColBetaMinus = -dColBetaPlus; float gradAlphaPlus = getManhattanGradient(m_dIx, m_dIy, row + dRowAlphaPlus, col + dColAphaPlus);