Through the Sobel operator I have been able to determine the gradient magnitude of an image. I display this below:
Now I wish to determine the gradient orientation. To do so, I am following this post, which makes use of the function cv2.phase
. Following this, angles are hard coded a particular colour depending on the returned degrees of the function. My issue is that the values this function returns for me are between the range of 0 and 90 degrees. Consequently, I am getting an image consisting of only red and cyan.
My code is as follows:
# where gray_blur is a grayscale image of dimension 512 by 512
# 3x3 sobel filters for edge detection
sobel_x = np.array([[ -1, 0, 1],
[ -2, 0, 2],
[ -1, 0, 1]])
sobel_y = np.array([[ -1, -2, -1],
[ 0, 0, 0],
[ 1, 2, 1]])
# Filter the blurred grayscale images using filter2D
filtered_blurred_x = cv2.filter2D(gray_blur, -1, sobel_x)
filtered_blurred_y = cv2.filter2D(gray_blur, -1, sobel_y)
# Compute the orientation of the image
orien = cv2.phase(np.array(filtered_blurred_x, np.float32), np.array(filtered_blurred_y, dtype=np.float32), angleInDegrees=True)
image_map = np.zeros((orien.shape[0], orien.shape[1], 3), dtype=np.int16)
# Define RGB colours
red = np.array([255, 0, 0])
cyan = np.array([0, 255, 255])
green = np.array([0, 255, 0])
yellow = np.array([255, 255, 0])
# Set colours corresponding to angles
for i in range(0, image_map.shape[0]):
for j in range(0, image_map.shape[1]):
if orien[i][j] < 90.0:
image_map[i, j, :] = red
elif orien[i][j] >= 90.0 and orien[i][j] < 180.0:
image_map[i, j, :] = cyan
elif orien[i][j] >= 180.0 and orien[i][j] < 270.0:
image_map[i, j, :] = green
elif orien[i][j] >= 270.0 and orien[i][j] < 360.0:
image_map[i, j, :] = yellow
# Display gradient orientation
f, ax1 = plt.subplots(1, 1, figsize=(20,10))
ax1.set_title('gradient orientation')
ax1.imshow(image_map)
Which displays the image:
Thanks in advance.
The ddepth
parameter of cv2.filter2D
is important. You set it to -1, meaning that the filtered image will have the same depth as the input. gray_blur
seems to be in an unsigned integer (probably uint8), thus the filter output is as well.
As your filter can produce negative values they are underflowing the uint8. Set the ddepth to receive the full value range from the filter:
filtered_blurred_x = cv2.filter2D(gray_blur, cv2.CV_32F, sobel_x)
filtered_blurred_y = cv2.filter2D(gray_blur, cv2.CV_32F, sobel_y)
With this your filtered images now encode a direction, and the orientation will map the full 360 degrees.
You can also use the HSV colorspace to encode orientation and magnitude, since Hue works with angles, and you can use the V to encode the magnitude.
For example, using your code and sexy Lenna:
import cv2 as cv
import numpy as np
im = cv.imread("lenna.png", -1)
im_gray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
k = 5
gray_blur = cv.bilateralFilter(im_gray, k, k * 2, k / 2) # To perserve edges
# 3x3 sobel filters for edge detection
sobel_x = np.array([[ -1, 0, 1],
[ -2, 0, 2],
[ -1, 0, 1]])
sobel_y = np.array([[ -1, -2, -1],
[ 0, 0, 0],
[ 1, 2, 1]])
# Filter the blurred grayscale images using filter2D
filtered_blurred_x = cv.filter2D(gray_blur, cv.CV_32F, sobel_x)
filtered_blurred_y = cv.filter2D(gray_blur, cv.CV_32F, sobel_y)
mag = cv.magnitude(filtered_blurred_x, filtered_blurred_y)
orien = cv.phase(filtered_blurred_x, filtered_blurred_y, angleInDegrees=True)
orien = orien / 2. # Go from 0:360 to 0:180
hsv = np.zeros_like(im)
hsv[..., 0] = orien # H (in OpenCV between 0:180)
hsv[..., 1] = 255 # S
hsv[..., 2] = cv.normalize(mag, None, 0, 255, cv.NORM_MINMAX) # V 0:255
bgr = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)
cv.imshow("Color coded edges", bgr)
cv.waitKey(0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With