addcolor
addcolor

Reputation: 503

Opencv unexpected output for Sobel operator

I am extracting the white stripes from this image but intrigued to see the output of basic Sobel operator in 'Lab' image. Although I am excited to see the black stripes as desired outcome, I am unable to justify what is going on behind 'np.hstack' operator. I am not getting same output if plt.imshow() is applied on just 'sobel'. The desired output is binary image containing white stripes.

enter image description here

import numpy as np
import cv2
import os,sys
from matplotlib import pyplot as plt    

def getColorSpaces(image):
    rgb = cv2.cvtColor(image,cv2.COLOR_RGB2BGR)
    gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)        
    return rgb,gray

def getImageDimnesion(image):
    height,width = image.shape[:2]        
    return height,width

def showImage(image,title,cmap):
    plt.imshow(image,cmap=cmap)
    plt.axis('off')
    plt.title(title)


def splitRGBChannels(image):
  red, green, blue= cv2.split(img)      
  return red, green, blue

def getMagnitude(gray):        
    sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
    sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
    abs_sobelx = np.absolute(sobelx)
    abs_sobely = np.absolute(sobely)        
    magnitude=np.sqrt(abs_sobelx*abs_sobelx+abs_sobely*abs_sobely)        
    return magnitude,np.arctan2(abs_sobely,abs_sobelx)


def applySobel(gray):        
    sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
    sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
    abs_sobelx = np.absolute(sobelx)
    abs_sobely = np.absolute(sobely)
    return abs_sobelx+abs_sobely


images_path=r'images'
images=os.listdir(images_path)

for im in images[:]:
    print(im)        
    img = cv2.imread(os.path.join(images_path,im))  

    plt.axis('off')
    plt.title('Originial')
    plt.imshow(img,cmap='gray') 
    plt.show()

for im in images[:]:
    print(im)
    plt.figure(figsize=(12, 12))
    img = cv2.imread(os.path.join(images_path,im))    

    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    lab=cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
    h,s,v = cv2.split(hsv)
    l,a,b = cv2.split(lab)
    sobel=applySobel(lab)
    imgs_comb = np.hstack([img,lab,sobel])

    plt.axis('off')
    plt.title('Originial-Lab-Sobel')
    plt.imshow(imgs_comb,cmap='gray') 
    plt.show()

EDIT1

plt.axis('off')
plt.title('img')
plt.imshow(img,cmap='gray') 
plt.show()

plt.axis('off')
plt.title('lab')
plt.imshow(lab,cmap='gray') 
plt.show()

plt.axis('off')
plt.title('sobel')
plt.imshow(sobel,cmap='gray') 
plt.show()

enter image description here

plt.axis('off')
plt.title('hstack')
plt.imshow(imgs_comb,cmap='gray')  #<<<<<Different output but is generic when tried with different images
plt.show()

enter image description here

Upvotes: 1

Views: 1114

Answers (2)

Jonathan Feenstra
Jonathan Feenstra

Reputation: 2779

Your applySobel method expects a grey scale (single channel) image as input, but you are using lab (3-channel image) as input, which will apply Sobel-filtering to all 3 channels. The unexpected result comes from plt.imshow interpreting the Sobel-filtered Lab-channels as the RGB-channels of your image.

It works as intended if you only use l, a or b instead (or a different method to convert Lab to grey). However, the result will not be binary. To make it binary, you can apply a threshold (using cv2.threshold(img, threshold, max_value, cv2.THRESH_BINARY). Here is an example:

import cv2
import numpy as np
from matplotlib import pyplot as plt
from skimage.io import imread


def applySobel(gray):
    sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
    sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
    abs_sobelx = np.absolute(sobelx)
    abs_sobely = np.absolute(sobely)
    return abs_sobelx + abs_sobely


# Load the image (RGB)
img = imread('https://i.sstatic.net/qN2ta.jpg')

# Convert to Lab and split channels
lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)
l, a, b = cv2.split(lab)

# Plot image of Lab-channels
plt.title('L, a, and b channel')
plt.imshow(np.hstack([l, a, b]), cmap='gray')
plt.show()

# Apply Sobel to L-channel (the other channels have low contrast)
l_sobel = applySobel(l)

# Plot result
plt.title('Sobel-filtered L-channel')
plt.imshow(l_sobel, cmap='gray')
plt.show()

# Make result binary by applying a threshold
sobel_thresh = np.uint8(cv2.threshold(l_sobel, 500, 255, cv2.THRESH_BINARY)[1])

# Plot binary result
plt.title('Thresholded Sobel-filtered L-channel')
plt.imshow(sobel_thresh, cmap='gray')
plt.show()

This results in the following images:

LAB channels Sobel-filtered L-channel Thresholded Sobel-filtered L-channel

The Sobel filter is used for edge detection, so it will only highlight the edges instead of the whole stripes. So if your goal is to highlight the whole stripes, directly thresholding the L-channel would be more effective:

# Directly threshold L-channel and plot
plt.imshow(cv2.threshold(l, 220, 255, cv2.THRESH_BINARY)[1], cmap='gray')
plt.show()

Result:

enter image description here

Also note that you cannot directly use np.hstack to combine 3-channel images with grey scale/binary images because of the different dimensions. First use np.stack((img,) * 3, axis=-1) to convert single channel images to 3-channel images.

Upvotes: 2

87VN0
87VN0

Reputation: 785

  • The np.hstack function is not changing your data but it changes the way the images are displayed. If you concatenate images of different range into one array, the imshow function will automatically scale to the maximum range. This is why you have a difference when you display only one or several images
  • The Sobel function that you designed is proper but:
    • It will be applied to all channels of your lab array. Maybe you would rather like to apply it only on the l channel. But if you check separately the range of l, a and b, you will notice that the intensity there is quite different.
    • When you display the result sobel, imshow will automatically assume that the 3 channels that you give are RGB data, this is why you have this weird image.
    • If you simply display plt.imshow(sobel[:, :, 0]), you get the results of the sobel filter, so you get the contours.
    • If you aim to get the stripes and not the contours you probably have to use mainly the intensity and not the contours.
  • I would advise you to look on github algorithms for deep learning lane detection, this kind of algorithms could outperform what you can do with simple operators. Often, the repositories share the models and it's not difficult to run the model.

Upvotes: 1

Related Questions