33#include <visp3/core/vpCannyEdgeDetection.h>
35#include <visp3/core/vpImageConvert.h>
40 : m_gaussianKernelSize(3)
42 , m_areGradientAvailable(false)
43 , m_lowerThreshold(-1.)
44 , m_upperThreshold(-1.)
46 initGaussianFilters();
50 ,
const float &lowerThreshold,
const float &upperThreshold)
51 : m_gaussianKernelSize(gaussianKernelSize)
52 , m_gaussianStdev(gaussianStdev)
53 , m_areGradientAvailable(false)
54 , m_lowerThreshold(lowerThreshold)
55 , m_upperThreshold(upperThreshold)
57 initGaussianFilters();
60#ifdef VISP_HAVE_NLOHMANN_JSON
69 std::ifstream file(jsonPath);
72 ss <<
"Problem opening file " << jsonPath <<
". Make sure it exists and is readable" << std::endl;
77 j = json::parse(file);
79 catch (json::parse_error &e) {
80 std::stringstream msg;
81 msg <<
"Could not parse JSON file : \n";
82 msg << e.what() << std::endl;
83 msg <<
"Byte position of error: " << e.byte;
88 initGaussianFilters();
93vpCannyEdgeDetection::initGaussianFilters()
95 if ((m_gaussianKernelSize % 2) == 0) {
98 m_fg.
resize(1, (m_gaussianKernelSize + 1)/2);
100 m_fgDg.
resize(1, (m_gaussianKernelSize + 1)/2);
105#ifdef HAVE_OPENCV_CORE
128 m_edgeCandidateAndGradient.clear();
129 m_edgePointsCandidates.clear();
132 if (!m_areGradientAvailable) {
133 performFilteringAndGradientComputation(I);
135 m_areGradientAvailable =
false;
138 performEdgeThining();
141 float upperThreshold = m_upperThreshold;
143 float lowerThreshold = m_lowerThreshold;
144 if (m_lowerThreshold < 0) {
146 lowerThreshold = m_upperThreshold / 3.f;
149 performHysteresisThresholding(lowerThreshold, upperThreshold);
152 performEdgeTracking();
185getThetaQuadrant(
const float &absoluteTheta,
int &dRowGradPlus,
int &dRowGradMinus,
int &dColGradPlus,
int &dColGradMinus)
187 if (absoluteTheta < 22.5) {
191 dRowGradPlus = dRowGradMinus = 0;
194 else if (absoluteTheta >= 22.5 && absoluteTheta < 67.5) {
196 dRowGradMinus = dColGradMinus = -1;
197 dRowGradPlus = dColGradPlus = 1;
200 else if (absoluteTheta >= 67.5 && absoluteTheta < 112.5) {
202 dColGradMinus = dColGradPlus = 0;
207 else if (absoluteTheta >= 112.5 && absoluteTheta < 157.5) {
219 dRowGradMinus = dRowGradPlus = 0;
245 float dx = dIx[row][col];
246 float dy = dIy[row][col];
247 grad = std::abs(dx) + std::abs(dy);
265 float dx = dIx[row][col];
266 float dy = dIy[row][col];
268 if (std::abs(dx) < std::numeric_limits<float>::epsilon()) {
272 absoluteTheta =
static_cast<float>(
vpMath::deg(std::abs(std::atan(dy / dx))));
274 return absoluteTheta;
277vpCannyEdgeDetection::performEdgeThining()
284 for (
int row = 0; row < nbRows; row++) {
285 for (
int col = 0; col < nbCols; col++) {
287 float grad = getManhattanGradient(dIx, dIy, row, col);
289 if (grad < std::numeric_limits<float>::epsilon()) {
294 float absoluteTheta = getAbsoluteTheta(dIx, dIy, row, col);
298 int dRowGradPlus = 0, dRowGradMinus = 0;
299 int dColGradPlus = 0, dColGradMinus = 0;
300 int thetaQuadrant = getThetaQuadrant(absoluteTheta, dRowGradPlus, dRowGradMinus, dColGradPlus, dColGradMinus);
302 bool isGradientInTheSameDirection =
true;
303 std::vector<std::pair<int, int> > pixelsSeen;
304 std::pair<int, int> bestPixel(row, col);
305 float bestGrad = grad;
306 int rowCandidate = row + dRowGradPlus;
307 int colCandidate = col + dColGradPlus;
309 while (isGradientInTheSameDirection) {
311 float gradPlus = getManhattanGradient(dIx, dIy, rowCandidate, colCandidate);
312 if (std::abs(gradPlus) < std::numeric_limits<float>::epsilon()) {
314 isGradientInTheSameDirection =
false;
317 int dRowGradPlusCandidate = 0, dRowGradMinusCandidate = 0;
318 int dColGradPlusCandidate = 0, dColGradMinusCandidate = 0;
319 float absThetaPlus = getAbsoluteTheta(dIx, dIy, rowCandidate, colCandidate);
320 int thetaQuadrantCandidate = getThetaQuadrant(absThetaPlus, dRowGradPlusCandidate, dRowGradMinusCandidate, dColGradPlusCandidate, dColGradMinusCandidate);
321 if (thetaQuadrantCandidate != thetaQuadrant) {
322 isGradientInTheSameDirection =
false;
326 std::pair<int, int> pixelCandidate(rowCandidate, colCandidate);
327 if (gradPlus > bestGrad) {
331 pixelsSeen.push_back(bestPixel);
332 bestPixel = pixelCandidate;
336 pixelsSeen.push_back(pixelCandidate);
338 rowCandidate += dRowGradPlus;
339 colCandidate += dColGradPlus;
343 m_edgeCandidateAndGradient[bestPixel] = bestGrad;
346 for (std::vector<std::pair<int, int> >::iterator it = pixelsSeen.begin(); it != pixelsSeen.end(); it++) {
348 int row_temp = it->first;
349 int col_temp = it->second;
350 dIx[row_temp][col_temp] = 0.;
351 dIy[row_temp][col_temp] = 0.;
358vpCannyEdgeDetection::performHysteresisThresholding(
const float &lowerThreshold,
const float &upperThreshold)
360 std::map<std::pair<unsigned int, unsigned int>,
float>::iterator it;
361 for (it = m_edgeCandidateAndGradient.begin(); it != m_edgeCandidateAndGradient.end(); it++) {
362 if (it->second >= upperThreshold) {
363 m_edgePointsCandidates[it->first] = STRONG_EDGE;
365 else if (it->second >= lowerThreshold && it->second < upperThreshold) {
366 m_edgePointsCandidates[it->first] = WEAK_EDGE;
372vpCannyEdgeDetection::performEdgeTracking()
374 std::map<std::pair<unsigned int, unsigned int>, EdgeType>::iterator it;
375 for (it = m_edgePointsCandidates.begin(); it != m_edgePointsCandidates.end(); it++) {
376 if (it->second == STRONG_EDGE) {
377 m_edgeMap[it->first.first][it->first.second] = 255;
379 else if (recursiveSearchForStrongEdge(it->first)) {
380 m_edgeMap[it->first.first][it->first.second] = 255;
386vpCannyEdgeDetection::recursiveSearchForStrongEdge(
const std::pair<unsigned int, unsigned int> &coordinates)
388 bool hasFoundStrongEdge =
false;
391 m_edgePointsCandidates[coordinates] = ON_CHECK;
392 for (
int dr = -1; dr <= 1 && !hasFoundStrongEdge; dr++) {
393 for (
int dc = -1; dc <= 1 && !hasFoundStrongEdge; dc++) {
394 int idRow = dr + (int)coordinates.first;
395 int idCol = dc + (int)coordinates.second;
398 if ((idRow < 0 || idRow >= nbRows)
399 || (idCol < 0 || idCol >= nbCols)
400 || (dr == 0 && dc == 0)
406 std::pair<unsigned int, unsigned int> key_candidate(idRow, idCol);
408 EdgeType type_candidate = m_edgePointsCandidates.at(key_candidate);
409 if (type_candidate == STRONG_EDGE) {
411 hasFoundStrongEdge =
true;
413 else if (type_candidate == WEAK_EDGE) {
414 hasFoundStrongEdge = recursiveSearchForStrongEdge(key_candidate);
422 if (hasFoundStrongEdge) {
423 m_edgePointsCandidates[coordinates] = STRONG_EDGE;
424 m_edgeMap[coordinates.first][coordinates.second] = 255;
426 return hasFoundStrongEdge;
Type * data
Address of the first element of the data array.
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
vpImage< unsigned char > detect(const vpImage< vpRGBa > &I_color)
Detect the edges in an image. Convert the color image into a gray-scale image.
void initFromJSON(const std::string &jsonPath)
Initialize all the algorithm parameters using the JSON file whose path is jsonPath....
vpCannyEdgeDetection()
Default constructor of the vpCannyEdgeDetection class. The thresholds used during the hysteresis thre...
error that can be emitted by ViSP classes.
@ badValue
Used to indicate that a value is not in the allowed range.
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static void getGradYGauss2D(const vpImage< ImageType > &I, vpImage< FilterType > &dIy, const FilterType *gaussianKernel, const FilterType *gaussianDerivativeKernel, unsigned int size)
static void getGradXGauss2D(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *gaussianKernel, const FilterType *gaussianDerivativeKernel, unsigned int size)
static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
Definition of the vpImage class member functions.
unsigned int getWidth() const
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
unsigned int getCols() const
unsigned int getHeight() const
unsigned int getRows() const
static double deg(double rad)