diff --git a/modules/core/include/visp3/core/vpCannyEdgeDetection.h b/modules/core/include/visp3/core/vpCannyEdgeDetection.h index 18f4920c5b..4bc4ebedfa 100644 --- a/modules/core/include/visp3/core/vpCannyEdgeDetection.h +++ b/modules/core/include/visp3/core/vpCannyEdgeDetection.h @@ -116,8 +116,10 @@ class VISP_EXPORT vpCannyEdgeDetection * \brief Step 3: Edge thining. * \details Perform the edge thining step. * Perform a non-maximum suppression to keep only local maxima as edge candidates. + * \param[in] lowerThreshold Edge candidates that are below this threshold are definitely not + * edges. */ - void performEdgeThining(); + void performEdgeThinning(const float &lowerThreshold); /** * \brief Perform hysteresis thresholding. diff --git a/modules/core/src/image/vpCannyEdgeDetection.cpp b/modules/core/src/image/vpCannyEdgeDetection.cpp index 274ba7f6b6..84f955f491 100644 --- a/modules/core/src/image/vpCannyEdgeDetection.cpp +++ b/modules/core/src/image/vpCannyEdgeDetection.cpp @@ -186,9 +186,6 @@ vpCannyEdgeDetection::detect(const vpImage &I) m_areGradientAvailable = false; // Reset for next call // // Step 3: edge thining - performEdgeThining(); - - // // Step 4: hysteresis thresholding float upperThreshold = m_upperThreshold; float lowerThreshold = m_lowerThreshold; if (upperThreshold < 0) { @@ -200,7 +197,11 @@ vpCannyEdgeDetection::detect(const vpImage &I) // Applying Canny recommendation to have the upper threshold 3 times greater than the lower threshold. lowerThreshold = m_upperThreshold / 3.f; } + // To ensure that if lowerThreshold = 0, we reject null gradient points + lowerThreshold = std::max(lowerThreshold, std::numeric_limits::epsilon()); + performEdgeThinning(lowerThreshold); + // // Step 4: hysteresis thresholding performHysteresisThresholding(lowerThreshold, upperThreshold); // // Step 5: edge tracking @@ -220,65 +221,69 @@ vpCannyEdgeDetection::performFilteringAndGradientComputation(const vpImage(GIx, Iblur, m_fg.data, m_gaussianKernelSize); // Computing the gradients - vpImageFilter::filter(Iblur, m_dIx, m_gradientFilterX); - vpImageFilter::filter(Iblur, m_dIy, m_gradientFilterY); + vpImageFilter::filter(Iblur, m_dIx, m_gradientFilterX, true); + vpImageFilter::filter(Iblur, m_dIy, m_gradientFilterY, true); } else { - std::string errmsg("Currently, the only filtering and gradient operators are Gaussian blur + Sobel"); + std::string errmsg("Currently, the filtering operation \""); + errmsg += vpImageFilter::vpCannyFilteringAndGradientTypeToString(m_filteringAndGradientType); + errmsg += "\" is not handled."; throw(vpException(vpException::notImplementedError, errmsg)); } } /** - * \brief Get the theta quadrant in which lies absoluteTheta and the offset along the horizontal - * and vertical direction where to look for the neighbors. + * \brief Get the interpolation weights and offsets. * * \param[in] absoluteTheta : The absolute value of the angle of the edge, expressed in degrees. - * \param[out] dRowGradPlus : The offset in the vertical positive direction. - * \param[out] dRowGradMinus : The offset in the vertical negative direction. - * \param[out] dColGradPlus : The offset in the horizontal positive direction. - * \param[out] dColGradMinus : The offset in the horizontal negative direction. - * \return The quadrant in which lies the angle of the edge, expressed in degrees. + * \param[out] alpha : The weight of the first point used for the interpolation. + * \param[out] beta : The weight of the second point used for the interpolation. + * \param[out] dRowGradAlpha : The offset along the row attached to the alpha weight. + * \param[out] dRowGradBeta : The offset along the row attached to the beta weight. + * \param[out] dColGradAlpha : The offset along the column attached to the alpha weight. + * \param[out] dColGradBeta : The offset along the column attached to the beta weight. */ -int -getThetaQuadrant(const float &absoluteTheta, int &dRowGradPlus, int &dRowGradMinus, int &dColGradPlus, int &dColGradMinus) +void +getInterpolationWeightsAndOffsets(const float &absoluteTheta, + float &alpha, float &beta, + int &dRowGradAlpha, int &dRowGradBeta, + int &dColGradAlpha, int &dColGradBeta +) { - if (absoluteTheta < 22.5) { - // Angles between -22.5 and 22.5 are mapped to be horizontal axis - dColGradMinus = -1; - dColGradPlus = 1; - dRowGradPlus = dRowGradMinus = 0; - return 0; + float thetaMin = 0.f; + if (absoluteTheta < 45.f) { + // Angles between 0 and 45 deg rely on the horizontal and diagonal points + dColGradAlpha = 1; + dColGradBeta = 1; + dRowGradAlpha = 0; + dRowGradBeta = -1; } - else if (absoluteTheta >= 22.5 && absoluteTheta < 67.5) { - // Angles between 22.5 and 67.5 are mapped to the diagonal 45degree - dRowGradMinus = dColGradMinus = -1; - dRowGradPlus = dColGradPlus = 1; - return 45; + else if (absoluteTheta >= 45.f && absoluteTheta < 90.f) { + // Angles between 45 and 90 deg rely on the diagonal and vertical points + thetaMin = 45.f; + dColGradAlpha = 1; + dColGradBeta = 0; + dRowGradAlpha = -1; + dRowGradBeta = -1; } - else if (absoluteTheta >= 67.5 && absoluteTheta < 112.5) { - // Angles between 67.5 and 112.5 are mapped to the vertical axis - dColGradMinus = dColGradPlus = 0; - dRowGradMinus = -1; - dRowGradPlus = 1; - return 90; + else if (absoluteTheta >= 90.f && absoluteTheta < 135.f) { + // Angles between 90 and 135 deg rely on the vertical and diagonal points + thetaMin = 90.f; + dColGradAlpha = 0; + dColGradBeta = -1; + dRowGradAlpha = -1; + dRowGradBeta = -1; } - else if (absoluteTheta >= 112.5 && absoluteTheta < 157.5) { - // Angles between 112.5 and 157.5 are mapped to the diagonal -45degree - dRowGradMinus = -1; - dColGradMinus = 1; - dRowGradPlus = 1; - dColGradPlus = -1; - return 135; + else if (absoluteTheta >= 135.f && absoluteTheta < 180.f) { + // Angles between 135 and 180 deg rely on the vertical and diagonal points + thetaMin = 135.f; + dColGradAlpha = -1; + dColGradBeta = -1; + dRowGradAlpha = -1; + dRowGradBeta = 0; } - else { - // Angles greater than 157.5 are mapped to be horizontal axis - dColGradMinus = 1; - dColGradPlus = -1; - dRowGradMinus = dRowGradPlus = 0; - return 180; - } - return -1; // Should not reach this point + beta = (absoluteTheta - thetaMin) / 45.f; + alpha = 1.f - beta; } /** @@ -320,123 +325,55 @@ getManhattanGradient(const vpImage &dIx, const vpImage &dIy, const float getAbsoluteTheta(const vpImage &dIx, const vpImage &dIy, const int &row, const int &col) { - float absoluteTheta; + float absoluteTheta = 0.f; float dx = dIx[row][col]; float dy = dIy[row][col]; if (std::abs(dx) < std::numeric_limits::epsilon()) { - absoluteTheta = 90.; + absoluteTheta = 90.f; } else { - absoluteTheta = static_cast(vpMath::deg(std::abs(std::atan(dy / dx)))); + absoluteTheta = static_cast(vpMath::deg(std::abs(std::atan2(dy, dx)))); } return absoluteTheta; } -/** - * \brief Search in the direction of the gradient for the highest value of the gradient. - * - * \param[in] dIx The gradient image along the x-axis. - * \param[in] dIy The gradient image along the y-axis. - * \param[in] row The row of the initial point that is considered. - * \param[in] col The column of the initial point that is considered. - * \param[in] thetaQuadrant The gradient orientation quadrant of the initial point. - * \param[in] dRowGrad The direction of the gradient for the vertical direction. - * \param[in] dColGrad The direction of the gradient for the horizontal direction. - * \param[out] pixelsSeen The list of pixels that are of same gradient orientation quadrant. - * \param[out] bestPixel The pixel having the highest absolute value of gradient. - * \param[out] bestGrad The highest absolute value of gradient. - */ void -searchForBestGradientInGradientDirection(const vpImage &dIx, const vpImage &dIy, -const int &row, const int &col, const int &thetaQuadrant, const int &dRowGrad, const int &dColGrad, -std::vector > &pixelsSeen, std::pair &bestPixel, float &bestGrad) +vpCannyEdgeDetection::performEdgeThinning(const float &lowerThreshold) { - bool isGradientInTheSameDirection = true; - int rowCandidate = row + dRowGrad; - int colCandidate = col + dColGrad; - - while (isGradientInTheSameDirection) { - // Getting the gradients around the edge point - float gradPlus = getManhattanGradient(dIx, dIy, rowCandidate, colCandidate); - if (std::abs(gradPlus) < std::numeric_limits::epsilon()) { - // The gradient is almost null => ignoring the point - isGradientInTheSameDirection = false; - break; - } - int dRowGradPlusCandidate = 0, dRowGradMinusCandidate = 0; - int dColGradPlusCandidate = 0, dColGradMinusCandidate = 0; - float absThetaPlus = getAbsoluteTheta(dIx, dIy, rowCandidate, colCandidate); - int thetaQuadrantCandidate = getThetaQuadrant(absThetaPlus, dRowGradPlusCandidate, dRowGradMinusCandidate, dColGradPlusCandidate, dColGradMinusCandidate); - if (thetaQuadrantCandidate != thetaQuadrant) { - isGradientInTheSameDirection = false; - break; - } - - std::pair pixelCandidate(rowCandidate, colCandidate); - if (gradPlus > bestGrad) { - // The gradient is higher with the next pixel candidate - // Saving it - bestGrad = gradPlus; - pixelsSeen.push_back(bestPixel); - bestPixel = pixelCandidate; - } - else { - // Best pixel is still the best - pixelsSeen.push_back(pixelCandidate); - } - rowCandidate += dRowGrad; - colCandidate += dColGrad; - } -} - -void -vpCannyEdgeDetection::performEdgeThining() -{ - vpImage dIx = m_dIx; - vpImage dIy = m_dIy; int nbRows = m_dIx.getRows(); int nbCols = m_dIx.getCols(); for (int row = 0; row < nbRows; row++) { for (int col = 0; col < nbCols; col++) { // Computing the gradient orientation and magnitude - float grad = getManhattanGradient(dIx, dIy, row, col); + float grad = getManhattanGradient(m_dIx, m_dIy, row, col); - if (grad < std::numeric_limits::epsilon()) { - // The gradient is almost null => ignoring the point + if (grad < lowerThreshold) { + // The gradient is lower than minimum threshold => ignoring the point continue; } - float absoluteTheta = getAbsoluteTheta(dIx, dIy, row, col); - // Getting the offset along the horizontal and vertical axes // depending on the gradient orientation - int dRowGradPlus = 0, dRowGradMinus = 0; - int dColGradPlus = 0, dColGradMinus = 0; - int thetaQuadrant = getThetaQuadrant(absoluteTheta, dRowGradPlus, dRowGradMinus, dColGradPlus, dColGradMinus); - - std::vector > pixelsSeen; - std::pair bestPixel(row, col); - float bestGrad = grad; - - // iterate over all the pixels having the same gradient orientation quadrant - searchForBestGradientInGradientDirection(dIx, dIy, row, col, thetaQuadrant, dRowGradPlus, dColGradPlus, - pixelsSeen, bestPixel, bestGrad); - - searchForBestGradientInGradientDirection(dIx, dIy, row, col, thetaQuadrant, dRowGradMinus, dColGradMinus, - pixelsSeen, bestPixel, bestGrad); - - // Keeping the edge point that has the highest gradient - m_edgeCandidateAndGradient[bestPixel] = bestGrad; - - // Suppressing non-maximum gradient - for (std::vector >::iterator it = pixelsSeen.begin(); it != pixelsSeen.end(); it++) { - // Suppressing non-maximum gradient - int row_temp = it->first; - int col_temp = it->second; - dIx[row_temp][col_temp] = 0.; - dIy[row_temp][col_temp] = 0.; + int dRowAlphaPlus = 0, dRowBetaPlus = 0; + int dColAphaPlus = 0, dColBetaPlus = 0; + float absTheta = getAbsoluteTheta(m_dIx, m_dIy, row, col); + float alpha = 0.f, beta = 0.f; + getInterpolationWeightsAndOffsets(absTheta, 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); + float gradBetaPlus = getManhattanGradient(m_dIx, m_dIy, row + dRowBetaPlus, col + dColBetaPlus); + float gradAlphaMinus = getManhattanGradient(m_dIx, m_dIy, row + dRowAlphaMinus, col + dColAphaMinus); + float gradBetaMinus = getManhattanGradient(m_dIx, m_dIy, row + dRowBetaMinus, col + dColBetaMinus); + float gradPlus = alpha * gradAlphaPlus + beta * gradBetaPlus; + float gradMinus = alpha * gradAlphaMinus + beta * gradBetaMinus; + + if (grad >= gradPlus && grad >= gradMinus) { + // Keeping the edge point that has the highest gradient + std::pair bestPixel(row, col); + m_edgeCandidateAndGradient[bestPixel] = grad; } } } @@ -482,7 +419,9 @@ vpCannyEdgeDetection::recursiveSearchForStrongEdge(const std::pair= nbRows) @@ -501,6 +440,7 @@ vpCannyEdgeDetection::recursiveSearchForStrongEdge(const std::pair #if defined(VISP_HAVE_X11) -vpDisplayX drawingHelpers::d; +vpDisplayX drawingHelpers::d_Iinput; +vpDisplayX drawingHelpers::d_dIx; +vpDisplayX drawingHelpers::d_dIy; +vpDisplayX drawingHelpers::d_IcannyVisp; +vpDisplayX drawingHelpers::d_IcannyImgFilter; #elif defined(HAVE_OPENCV_HIGHGUI) -vpDisplayOpenCV drawingHelpers::d; +vpDisplayOpenCV drawingHelpers::d_Iinput; +vpDisplayOpenCV drawingHelpers::d_dIx; +vpDisplayOpenCV drawingHelpers::d_dIy; +vpDisplayOpenCV drawingHelpers::d_IcannyVisp; +vpDisplayOpenCV drawingHelpers::d_IcannyImgFilter; #elif defined(VISP_HAVE_GTK) -vpDisplayGTK drawingHelpers::d; +vpDisplayGTK drawingHelpers::d_Iinput; +vpDisplayGTK drawingHelpers::d_dIx; +vpDisplayGTK drawingHelpers::d_dIy; +vpDisplayGTK drawingHelpers::d_IcannyVisp; +vpDisplayGTK drawingHelpers::d_IcannyImgFilter; #elif defined(VISP_HAVE_GDI) -vpDisplayGDI drawingHelpers::d; +vpDisplayGDI drawingHelpers::d_Iinput; +vpDisplayGDI drawingHelpers::d_dIx; +vpDisplayGDI drawingHelpers::d_dIy; +vpDisplayGDI drawingHelpers::d_IcannyVisp; +vpDisplayGDI drawingHelpers::d_IcannyImgFilter; #elif defined(VISP_HAVE_D3D9) -vpDisplayD3D drawingHelpers::d; +vpDisplayD3D drawingHelpers::d_Iinput; +vpDisplayD3D drawingHelpers::d_dIx; +vpDisplayD3D drawingHelpers::d_dIy; +vpDisplayD3D drawingHelpers::d_IcannyVisp; +vpDisplayD3D drawingHelpers::d_IcannyImgFilter; #endif -vpImage drawingHelpers::I_disp; - -bool drawingHelpers::display(vpImage &I, const std::string &title, const bool &blockingMode) +void drawingHelpers::init(vpImage &Iinput, vpImage &IcannyVisp, vpImage *p_dIx, + vpImage *p_dIy, vpImage *p_IcannyimgFilter) { - I_disp = I; -#if defined(VISP_HAVE_DISPLAY) - if (!d.isInitialised()) { - d.init(I_disp); - vpDisplay::setTitle(I_disp, title); + d_Iinput.init(Iinput, 10, 10); + d_IcannyVisp.init(IcannyVisp, 10, Iinput.getHeight() + 10 * 2); + if (p_dIx != nullptr) { + d_dIx.init(*p_dIx, Iinput.getWidth() + 2 * 10, 10); } -#else - (void)title; -#endif + if (p_dIy != nullptr) { + d_dIy.init(*p_dIy, 2 * Iinput.getWidth() + 3 * 10, 10); + } + if (p_IcannyimgFilter != nullptr) { + d_IcannyImgFilter.init(*p_IcannyimgFilter, Iinput.getWidth() + 2 * 10, Iinput.getHeight() + 10 * 2); + } +} - vpDisplay::display(I_disp); - vpDisplay::displayText(I_disp, 15, 15, "Left click to continue...", vpColor::red); - vpDisplay::displayText(I_disp, 35, 15, "Right click to stop...", vpColor::red); - vpDisplay::flush(I_disp); +void drawingHelpers::display(vpImage &I, const std::string &title) +{ + vpDisplay::display(I); + vpDisplay::setTitle(I, title); + vpDisplay::flush(I); +} + +bool drawingHelpers::waitForClick(const vpImage &I, const bool &blockingMode) +{ + vpDisplay::displayText(I, 15, 15, "Left click to continue...", vpColor::red); + vpDisplay::displayText(I, 35, 15, "Right click to stop...", vpColor::red); + vpDisplay::flush(I); vpMouseButton::vpMouseButtonType button; - vpDisplay::getClick(I_disp, button, blockingMode); + vpDisplay::getClick(I, button, blockingMode); bool hasToContinue = true; if (button == vpMouseButton::button3) { // Right click => stop the program @@ -72,24 +102,3 @@ bool drawingHelpers::display(vpImage &I, const std::string &title, const return hasToContinue; } - -bool drawingHelpers::display(vpImage &D, const std::string &title, const bool &blockingMode) -{ - vpImage I; // Image to display - vpImageConvert::convert(D, I); - return display(I, title, blockingMode); -} - -bool drawingHelpers::display(vpImage &D, const std::string &title, const bool &blockingMode) -{ - vpImage I; // Image to display - vpImageConvert::convert(D, I); - return display(I, title, blockingMode); -} - -bool drawingHelpers::display(vpImage &F, const std::string &title, const bool &blockingMode) -{ - vpImage I; // Image to display - vpImageConvert::convert(F, I); - return display(I, title, blockingMode); -} diff --git a/tutorial/image/drawingHelpers.h b/tutorial/image/drawingHelpers.h index e612b20237..cc2619554a 100644 --- a/tutorial/image/drawingHelpers.h +++ b/tutorial/image/drawingHelpers.h @@ -38,66 +38,67 @@ namespace drawingHelpers { #if defined(VISP_HAVE_X11) -extern vpDisplayX d; +extern vpDisplayX d_Iinput; +extern vpDisplayX d_dIx; +extern vpDisplayX d_dIy; +extern vpDisplayX d_IcannyVisp; +extern vpDisplayX d_IcannyImgFilter; #elif defined(HAVE_OPENCV_HIGHGUI) -extern vpDisplayOpenCV d; +extern vpDisplayOpenCV d_Iinput; +extern vpDisplayOpenCV d_dIx; +extern vpDisplayOpenCV d_dIy; +extern vpDisplayOpenCV d_IcannyVisp; +extern vpDisplayOpenCV d_IcannyImgFilter; #elif defined(VISP_HAVE_GTK) -extern vpDisplayGTK d; +extern vpDisplayGTK d_Iinput; +extern vpDisplayGTK d_dIx; +extern vpDisplayGTK d_dIy; +extern vpDisplayGTK d_IcannyVisp; +extern vpDisplayGTK d_IcannyImgFilter; #elif defined(VISP_HAVE_GDI) -extern vpDisplayGDI d; +extern vpDisplayGDI d_Iinput; +extern vpDisplayGDI d_dIx; +extern vpDisplayGDI d_dIy; +extern vpDisplayGDI d_IcannyVisp; +extern vpDisplayGDI d_IcannyImgFilter; #elif defined(VISP_HAVE_D3D9) -extern vpDisplayD3D d; +extern vpDisplayD3D d_Iinput; +extern vpDisplayD3D d_dIx; +extern vpDisplayD3D d_dIy; +extern vpDisplayD3D d_IcannyVisp; +extern vpDisplayD3D d_IcannyImgFilter; #endif -extern vpImage I_disp; /*!< Displayed image.*/ - /** - * \brief Display a RGB image and catch the user clicks to know if - * the user wants to stop the program. + * \brief Initialize the different displays. * - * \param[out] I The RGB image to display. - * \param[in] title The title of the window. - * \param[in] blockingMode If true, wait for a click to switch to the next image. - * \return true The user wants to continue the application. - * \return false The user wants to stop the application. + * \param[out] Iinput Input image of the program. + * \param[out] IcannyVisp Image resulting from the vpCannyEdgeDetection method. + * \param[out] p_dIx If different from nullptr, pointer towards the gradient along the horizontal axis. + * \param[out] p_dIy If different from nullptr, pointer towards the gradient along the vertical axis. + * \param[out] p_IcannyimgFilter If different from nullptr, pointer towards the result of the vpImageFilter::canny + * method. */ -bool display(vpImage &I, const std::string &title, const bool &blockingMode); +void init(vpImage &Iinput, vpImage &IcannyVisp, vpImage *p_dIx, + vpImage *p_dIy, vpImage *p_IcannyimgFilter); /** - * \brief Display a gray-scale image and catch the user clicks to know if - * the user wants to stop the program. + * \brief Display a gray-scale image. * * \param[out] I The gray-scale image to display. * \param[in] title The title of the window. - * \param[in] blockingMode If true, wait for a click to switch to the next image. - * \return true The user wants to continue the application. - * \return false The user wants to stop the application. */ -bool display(vpImage &I, const std::string &title, const bool &blockingMode); +void display(vpImage &I, const std::string &title); /** - * \brief Display a double precision image and catch the user clicks to know if - * the user wants to stop the program. + * \brief Catch the user clicks to know if the user wants to stop the program. * - * \param[out] D The double precision image to display. - * \param[in] title The title of the window. - * \param[in] blockingMode If true, wait for a click to switch to the next image. - * \return true The user wants to continue the application. - * \return false The user wants to stop the application. - */ -bool display(vpImage &D, const std::string &title, const bool &blockingMode); - -/** - * \brief Display a floating-point precision image and catch the user clicks to know if - * the user wants to stop the program. - * - * \param[out] F The floating-point precision image to display. - * \param[in] title The title of the window. + * \param[in] I The gray-scale image to display. * \param[in] blockingMode If true, wait for a click to switch to the next image. * \return true The user wants to continue the application. * \return false The user wants to stop the application. */ -bool display(vpImage &F, const std::string &title, const bool &blockingMode); +bool waitForClick(const vpImage &I, const bool &blockingMode); } #endif diff --git a/tutorial/image/tutorial-canny.cpp b/tutorial/image/tutorial-canny.cpp index 2e74f9ac36..c5ece751ae 100644 --- a/tutorial/image/tutorial-canny.cpp +++ b/tutorial/image/tutorial-canny.cpp @@ -66,8 +66,11 @@ void computeMeanMaxStdev(const vpImage &I, float &mean, float &max, float &st stdev = std::sqrt(stdev); } -void setGradientOutsideClass(const vpImage &I, const int &gaussianKernelSize, const float &gaussianStdev, vpCannyEdgeDetection &cannyDetector, - const unsigned int apertureSize, const vpImageFilter::vpCannyFilteringAndGradientType &filteringType) +void setGradientOutsideClass(const vpImage &I, const int &gaussianKernelSize, const float &gaussianStdev, + vpCannyEdgeDetection &cannyDetector, const unsigned int apertureSize, + const vpImageFilter::vpCannyFilteringAndGradientType &filteringType, + vpImage &dIx_uchar, vpImage &dIy_uchar +) { // Computing the gradients vpImage dIx, dIy; @@ -82,11 +85,13 @@ void setGradientOutsideClass(const vpImage &I, const int &gaussia computeMeanMaxStdev(dIx, mean, max, stdev); std::string title = "Gradient along the horizontal axis. Mean = " + std::to_string(mean) + "+/-" + std::to_string(stdev) + " Max = " + std::to_string(max); - drawingHelpers::display(dIx, title, true); + vpImageConvert::convert(dIx, dIx_uchar); + drawingHelpers::display(dIx_uchar, title); computeMeanMaxStdev(dIy, mean, max, stdev); title = "Gradient along the horizontal axis. Mean = " + std::to_string(mean) + "+/-" + std::to_string(stdev) + " Max = " + std::to_string(max); - drawingHelpers::display(dIy, title, true); + vpImageConvert::convert(dIy, dIy_uchar); + drawingHelpers::display(dIy_uchar, title); } void usage(const std::string &softName, int gaussianKernelSize, float gaussianStdev, float lowerThresh, float upperThresh, @@ -222,7 +227,7 @@ int main(int argc, const char *argv[]) vpCannyEdgeDetection cannyDetector(opt_gaussianKernelSize, opt_gaussianStdev, opt_apertureSize, opt_lowerThresh, opt_upperThresh, opt_lowerThreshRatio, opt_upperThreshRatio, opt_filteringType); - vpImage I_canny_input; + vpImage I_canny_input, I_canny_visp, dIx_uchar, dIy_uchar, I_canny_imgFilter; if (!opt_img.empty()) { // Detection on the user image vpImageIo::read(I_canny_input, opt_img); @@ -237,24 +242,41 @@ int main(int argc, const char *argv[]) } } + // Initialization of the displays + I_canny_visp = I_canny_imgFilter = dIx_uchar = dIy_uchar = I_canny_input; + vpImage *p_dIx = nullptr, *p_dIy = nullptr, *p_IcannyImgFilter = nullptr; + + if (opt_gradientOutsideClass) { + p_dIx = &dIx_uchar; + p_dIy = &dIy_uchar; + } + + if (opt_useVpImageFilterCanny) { + p_IcannyImgFilter = &I_canny_imgFilter; + } + drawingHelpers::init(I_canny_input, I_canny_visp, p_dIx, p_dIy, p_IcannyImgFilter); + + // Computing the gradient outside the vpCannyEdgeDetection class if asked if (opt_gradientOutsideClass) { - setGradientOutsideClass(I_canny_input, opt_gaussianKernelSize, opt_gaussianStdev, cannyDetector, opt_apertureSize, opt_filteringType); + setGradientOutsideClass(I_canny_input, opt_gaussianKernelSize, opt_gaussianStdev, cannyDetector, opt_apertureSize, + opt_filteringType, dIx_uchar, dIy_uchar); } - vpImage I_canny = cannyDetector.detect(I_canny_input); + I_canny_visp = cannyDetector.detect(I_canny_input); float mean, max, stdev; computeMeanMaxStdev(I_canny_input, mean, max, stdev); std::string title("Input of the Canny edge detector. Mean = " + std::to_string(mean) + "+/-" + std::to_string(stdev) + " Max = " + std::to_string(max)); - drawingHelpers::display(I_canny_input, title, true); - drawingHelpers::display(I_canny, "Canny results on image " + opt_img, true); + drawingHelpers::display(I_canny_input, title); + drawingHelpers::display(I_canny_visp, "Canny results on image " + opt_img); if (opt_useVpImageFilterCanny) { float cannyThresh = opt_upperThresh; float lowerThresh(opt_lowerThresh); - vpImageFilter::canny(I_canny_input, I_canny, opt_gaussianKernelSize, lowerThresh, cannyThresh, + vpImageFilter::canny(I_canny_input, I_canny_imgFilter, opt_gaussianKernelSize, lowerThresh, cannyThresh, opt_apertureSize, opt_gaussianStdev, opt_lowerThreshRatio, opt_upperThreshRatio, true, opt_backend, opt_filteringType); - drawingHelpers::display(I_canny, "Canny results with \"" + vpImageFilter::vpCannyBackendTypeToString(opt_backend) + "\" backend", true); + drawingHelpers::display(I_canny_imgFilter, "Canny results with \"" + vpImageFilter::vpCannyBackendTypeToString(opt_backend) + "\" backend"); } + drawingHelpers::waitForClick(I_canny_input, true); return EXIT_SUCCESS; }