1

I'm currently following this tutorial as part of an university assignment where we are supposed to implement canny edge detection ourselfes. Applying the gaussian blur worked without any problems but now I'm trying to display the magnitude intensity as shown on the website.

I implemented the functions as seen on the mentioned website and created a function for running the canny edge detection. Currently this is what the function looks like:

def canny_edge(img):
    noise_reduction = cv.filter2D(img, -1, gaussian_kernel(5))
    cv.imshow('Blur', noise_reduction)
    magnitude, gradient = sobel_filters(img)
    magnitude = magnitude.astype(np.uint8)
    sobel = magnitude * gradient
    sobel = sobel.astype(np.uint8)
    test = img + (255 - gradient * noise_reduction)
    plt.imshow(gradient, cmap='gray')
    plt.show()
    cv.imshow('Gradient', magnitude)
    cv.imshow('Original Image', img)

I had to convert the magnitude and sobel arrays to np.uint8 as otherwise they'd contain float values which led to an error when displaying the image. Currently, I'm using the variable test to try various things, like gradient - noise_reduction, the line you see above, etc. The problem is that I always get images that look similar to these (image on the left shows test, image on the right shows gradient, image at the bottom shows magnitude):

enter image description here enter image description here enter image description here

I'm not too familiar with all the OpenCV functions that are available but I guess perhaps it might be important to use some of them that I don't know for this purpose. Unfortunately I haven't been able to find any information about how the magnitude returned by the sobel_filters function is applied to the image in the tutorial linked above. Thanks in advance for any input on how to solve this problem.

Samaranth
  • 385
  • 3
  • 16
  • I think you may need to add `.clip(0,255)` before `.astype(np.uint8)`. It looks like you are getting intensity wrap-around. – fmw42 May 26 '21 at 15:23
  • Ok, I threw in the line `magnitude = magnitude.clip(0,255)` directly before converting the array to `np.uint8` and I still get the same result, so I guess that wasn't the problem. I also assume that `.clip(0,255)` just ensures that the values are between 0 and 255 but as far as I remember, OpenCV also prints a warning to the console if values are above 255 and states something about automatically clipping them at 255. But thanks nonetheless for this idea. – Samaranth May 26 '21 at 16:15
  • What tool or code is providing `sobel_filters()`? Does this give x and y components of sobel gradient or the magnitude and direction? You label the output as magnitude and gradient. That does not seem correct to me for a sobel operation, which usually just provides the x and y components of the gradient. – fmw42 May 26 '21 at 16:43
  • The function is defined on the website that I linked. If I understood the website correctly, it applies the Sobel operator in x and y direction and returns "the magnitude G and the slope θ of the gradient". At least that's what's written directly above the code-snippet and actually corresponds to the variables that are being returned. And as the variables are calculated according to the mathematical formula that is given for the magnitude of the gradient and its direction that should indeed be what is provided by the function. – Samaranth May 26 '21 at 17:11
  • That all seems correct. Have you viewed each of your steps to be sure the results look reasonable? – fmw42 May 26 '21 at 20:28
  • @fmw42 When checking each of the steps I realized that I passed the original image instead of the blurred one to the `sobel_filters` function. However, even after passing the correct image I still get a similar result as before. I also ensured that the steps before look resonable. I did a direct comparison between blurred image and original image and it is indeed blurred. As this is the only actual step in the computation of the Edge Detection algorithm up to the point where I'm stuck, everything should work as presented on the website I assume. – Samaranth May 27 '21 at 08:22

1 Answers1

2

I think there might be an issue with ndimage.filters.convolve. I got similar results as you. But the following seems to work fine using Python/OpenCV

Input:

enter image description here

import cv2
import numpy as np
import skimage.exposure

img = cv2.imread('black_dress.png', cv2.IMREAD_GRAYSCALE)

img = cv2.GaussianBlur(img, (0,0), sigmaX=1.5, sigmaY=1.5)

Kx = np.array([[-1, 0, 1], 
               [-2, 0, 2], 
               [-1, 0, 1]])
Ky = np.array([[1,   2,  1], 
               [0,   0,  0], 
              [-1,  -2, -1]])

Ix = cv2.filter2D(img, -1, Kx)
Iy = cv2.filter2D(img, -1, Ky)

G = np.hypot(Ix, Iy)
G = skimage.exposure.rescale_intensity(G, in_range='image', out_range=(0,255)).astype(np.uint8)

theta = np.arctan2(Iy, Ix)
theta = skimage.exposure.rescale_intensity(theta, in_range='image', out_range=(0,255)).astype(np.uint8)
   
cv2.imwrite('black_dress_gradient_magnitude.png', G)
cv2.imwrite('black_dress_gradient_direction.png', theta)

cv2.imshow("magnitude", G)
cv2.imshow("direction", theta)
cv2.waitKey(0)

Magnitude:

enter image description here

Direction:

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • I can get scipy.ndimage.convolve to give similar results. One needs to make the input image float or at least I did after the Gaussian filter and that worked. – fmw42 May 27 '21 at 22:36
  • One other comment. OpenCV's filter2D does a correlate not a true convolution. So in scipy, you would need to use correlate not convolve to get the similar polarity, which would only matter for the direction, not the magnitude. – fmw42 May 27 '21 at 22:45