Sobel 运算符的 Opencv 意外输出
Opencv unexpected output for Sobel operator
我正在从此图像中提取白色条纹,但很想在 'Lab' 图像中看到基本 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()
您的 applySobel
方法需要灰度(单通道)图像作为输入,但您使用 lab
(3 通道图像)作为输入,这将对所有图像应用 Sobel 滤波3 个通道。意想不到的结果来自 plt.imshow
将 Sobel 过滤的 Lab 通道解释为图像的 RGB 通道。
如果您仅使用 l
、a
或 b
(或将 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 通道图像与灰色 scale/binary 图像组合。首先使用np.stack((img,) * 3, axis=-1)
将单通道图像转换为3通道图像。
np.hstack
函数不会更改您的数据,但会更改图像的显示方式。如果将不同范围的图像连接成一个数组,imshow
函数将自动缩放到最大范围。这就是为什么当你只显示一张或几张图片时会有差异
- 你设计的索贝尔函数是对的,但是:
- 它将应用于您的
lab
阵列的所有通道。也许您宁愿只在 l
频道上应用它。但是如果你分别查看l
、a
和b
的范围,你会发现那里的强度有很大的不同。
- 当你显示结果
sobel
时,imshow
会自动假设你给出的3个通道是RGB数据,这就是为什么你有这个奇怪的图像。
- 如果单纯显示
plt.imshow(sobel[:, :, 0])
,得到的是sobel过滤的结果,所以得到等高线
- 如果您的目标是获得条纹而不是轮廓,您可能必须主要使用强度而不是轮廓。
- 我建议您查看 github 用于深度学习车道检测的算法,这种算法的性能可能优于您使用简单运算符所能达到的效果。通常,存储库共享模型,运行 模型并不难。
我正在从此图像中提取白色条纹,但很想在 'Lab' 图像中看到基本 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()
您的 applySobel
方法需要灰度(单通道)图像作为输入,但您使用 lab
(3 通道图像)作为输入,这将对所有图像应用 Sobel 滤波3 个通道。意想不到的结果来自 plt.imshow
将 Sobel 过滤的 Lab 通道解释为图像的 RGB 通道。
如果您仅使用 l
、a
或 b
(或将 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 通道图像与灰色 scale/binary 图像组合。首先使用np.stack((img,) * 3, axis=-1)
将单通道图像转换为3通道图像。
np.hstack
函数不会更改您的数据,但会更改图像的显示方式。如果将不同范围的图像连接成一个数组,imshow
函数将自动缩放到最大范围。这就是为什么当你只显示一张或几张图片时会有差异- 你设计的索贝尔函数是对的,但是:
- 它将应用于您的
lab
阵列的所有通道。也许您宁愿只在l
频道上应用它。但是如果你分别查看l
、a
和b
的范围,你会发现那里的强度有很大的不同。 - 当你显示结果
sobel
时,imshow
会自动假设你给出的3个通道是RGB数据,这就是为什么你有这个奇怪的图像。 - 如果单纯显示
plt.imshow(sobel[:, :, 0])
,得到的是sobel过滤的结果,所以得到等高线 - 如果您的目标是获得条纹而不是轮廓,您可能必须主要使用强度而不是轮廓。
- 它将应用于您的
- 我建议您查看 github 用于深度学习车道检测的算法,这种算法的性能可能优于您使用简单运算符所能达到的效果。通常,存储库共享模型,运行 模型并不难。