【问题标题】:Opencv unexpected output for Sobel operatorSobel算子的Opencv意外输出
【发布时间】:2020-03-12 19:46:37
【问题描述】:

我正在从这张图片中提取白色条纹,但很想看到“实验室”图片中基本 Sobel 算子的输出。尽管我很高兴看到黑色条纹是理想的结果,但我无法证明“np.hstack”运算符背后发​​生的事情是正确的。如果 plt.imshow() 仅应用于“sobel”,我不会得到相同的输出。所需的输出是包含白色条纹的二进制图像。

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()

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

【问题讨论】:

    标签: python numpy opencv matplotlib sobel


    【解决方案1】:

    您的 applySobel 方法需要灰度(单通道)图像作为输入,但您使用 lab(3 通道图像)作为输入,这将对所有 3 个通道应用 Sobel 过滤。意外结果来自plt.imshow 将 Sobel 过滤的 Lab 通道解释为图像的 RGB 通道。

    如果您只使用 lab(或将 Lab 转换为灰色的其他方法),它会按预期工作。但是,结果不会是二进制的。要使其成为二进制,您可以应用一个阈值(使用cv2.threshold(img, threshold, max_value, cv2.THRESH_BINARY)。这是一个示例:

    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.stack.imgur.com/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()
    

    这会产生以下图像:

    Sobel 过滤器用于边缘检测,因此它只会突出边缘而不是整个条纹。因此,如果您的目标是突出整个条纹,则直接对 L 通道进行阈值处理会更有效:

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

    结果:

    另外请注意,由于尺寸不同,您不能直接使用 np.hstack 将 3 通道图像与灰度/二值图像组合。首先使用np.stack((img,) * 3, axis=-1)将单通道图像转换为3通道图像。

    【讨论】:

    • 感谢您解决此问题。我认为我的主要错误是不当使用 np.stack()
    • 注意到了一些差异。我正在使用 cv2.imread() 函数来读取图像。默认情况下,图像是 BGR 格式。编辑了我的查询,请检查。
    • plt.imshow 期望其输入为 RGB 格式。从 BGR 转换为 RGB,反之亦然,就像反转 numpy 数组的第三轴一样简单:rgb_img = bgr_img[:, :, ::-1]。如果您愿意,也可以使用rgb_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2RGB),以获得更好的可读性。
    【解决方案2】:
    • np.hstack 函数不会更改您的数据,但会更改图像的显示方式。如果将不同范围的图像连接到一个数组中,imshow 函数将自动缩放到最大范围。这就是为什么您只显示一张或多张图片时会有所不同的原因
    • 您设计的 Sobel 函数是正确的,但是:
      • 它将应用于lab 数组的所有通道。也许您宁愿只在l 频道上应用它。但是如果你分别查看lab的范围,你会发现那里的强度有很大的不同。
      • 当你显示结果sobel时,imshow会自动假设你给的3个通道是RGB数据,这就是为什么你有这个奇怪的图像。
      • 如果你只是显示plt.imshow(sobel[:, :, 0]),你会得到sobel过滤器的结果,所以你会得到轮廓。
      • 如果您的目标是获得条纹而不是轮廓,您可能必须主要使用强度而不是轮廓。
    • 我建议您查看 github 算法以进行深度学习车道检测,这种算法的性能可能优于您使用简单运算符所能完成的算法。通常,存储库会共享模型,并且运行模型并不困难。

    【讨论】:

      猜你喜欢
      • 2019-12-04
      • 2017-06-21
      • 2018-05-05
      • 2016-10-03
      • 2022-01-23
      • 2017-08-14
      • 2018-01-31
      • 1970-01-01
      • 2014-04-18
      相关资源
      最近更新 更多