3

I want to use OpenCV's Canny edge detector, such as is outlined in this question. For example:

cv::Canny(image,contours,10,350); 

However, I wish to not only get the final thresholded image out, but I also wish to get the detected edge angle at each pixel. Is this possible in OpenCV?

Community
  • 1
  • 1
Bill Cheatham
  • 11,396
  • 17
  • 69
  • 104

2 Answers2

3

canny doesn't give you this directly. However, you can calculate the angle from the Sobel transform, which is used internally in canny().

Pseudo code:

    cv::Canny(image,contours,10,350);
    cv::Sobel(image, dx, CV_64F, 1, 0, 3, 1, 0, cv::BORDER_REPLICATE);
    cv::Sobel(image, dy, CV_64F, 0, 1, 3, 1, 0, cv::BORDER_REPLICATE);

    cv::Mat angle(image.size(), CV_64F)

    foreach (i,j) such that contours[i, j] > 0
    {
        angle[i, j] = atan2(dy[i,j], dx[i , j])
    }
Bull
  • 11,771
  • 9
  • 42
  • 53
  • Canny calls Sobel internally, so this solution might be inefficient. If you go and take the code from opencv, you might write your own version of Canny that takes in input pre-computed sobel image – Antonio Feb 18 '14 at 11:46
3

Instead of using for loop you can also provide dx and dy gradients to phase function that returns grayscale image of angles direction, then pass it to applyColorMap function and then mask it with edges, so the background is black.

Here is the workflow:

  1. Get the angles

    Mat angles;
    phase(dx, dy, angles, true);
    

    true argument idicates that the angles are returned in degrees.

  2. Change the range of angles to 0-255 so you can convert to CV_8U without data loss

    angles = angles / 360 * 255;
    

    note that angles is still in CV_64F type as it comes from Sobel function

  3. Convert to CV_8U

    angles.convertTo(angles, CV_8U);
    
  4. Apply color map of your choice

    applyColorMap(angles, angles, COLORMAP_HSV);
    

    in this case I choose HSV colormap. See this for more info: https://www.learnopencv.com/applycolormap-for-pseudocoloring-in-opencv-c-python/

  5. Apply the edges mask so the background is black

    Mat colored;
    angles.copyTo(colored, contours);
    
  6. Finally display image :D

    imshow("Colored angles", colored);
    

In case your source is a video or webcam, before applying the mask of edges you addtionlly must clear colored image, to prevent aggregation:

colored.release();
angles.copyTo(colored, contours);

Full code here:

Mat angles, colored;

phase(dx, dy, angles, true);
angles = angles / 360 * 255;
angles.convertTo(angles, CV_8U);
applyColorMap(angles, angles, COLORMAP_HSV);
colored.release();
angles.copyTo(colored, contours);
imshow("Colored angles", colored);
Karol Daniluk
  • 506
  • 4
  • 10
  • Am I correct in saying that you can only cover a range of 180 degrees here, since the range from 180-359 is mapped to 0-179? – dthal Oct 20 '21 at 07:31
  • No, this should work for all range of 0-359 degrees. The color map function maps every value from range of 0 to 255, thats why we scale down the range of degrees from 0-360 to 0-255. So now value of 360deg is 255, 180deg is 128 and so on. – Karol Daniluk Oct 21 '21 at 09:53