手动实现OpenCV Sobel函数

3

我的图片

如果我有一张图片,并使用OpenCV的Sobel函数:

Sobel(Img,gradX,CV_16S,1,0,3);
convertScaleAbs(gradX,absGradX);
imshow("Gradient Image",absGradX);

我得到了一张漂亮的渐变图片

我想使用自定义核函数的filter2D来计算我的x梯度。我的Sobel核函数是 1 0 -1 2 0 -2 1 0 -1 现在当我尝试这样做时,我只得到了一张全黑的图片。

float  xVals[9] = {.125,0,-.125,.25,0,-.25,.125,0,-.125};
Mat xKernel = Mat(3,3,CV_32F,xVals);
Mat gradXManual,absGradXManual;
filter2D(Img,gradXManual,-1,xKernel,Point(-1,-1),0,BORDER_DEFAULT);
convertScaleAbs(gradXManual,absGradXManual);
imshow("Manual Gradient",absGradXManual);

生成的梯度图像全是黑色的。有什么想法我做错了什么吗? 谢谢。


1
使用您的自定义内核,我得到了一个输出。您能否在此处放置您正在使用的图像?但是,我正在使用Python OpenCV,但调用函数的方式与C++版本几乎相同。我使用了一张测试图片,从Sobel和您的自定义内核中都获得了输出。 - rayryeng
我现在会检查。请稍等一下。 - rayryeng
1
我看到Sobel实现和你的梯度之间的主要区别在于,你通过将每个元素除以8来获取Sobel核。因此,任何你检测到的梯度都会有一个对比度降低的效果,这就是我看到的。实际上,你基本上是将梯度结果除以8,因此你通过一个因子8减少了输出的强度。尝试使用float xVals [9] = {1f,0f,-1f,2f,0f,-2f,1f,0f,-1f}; 实际 Sobel核,然后再次运行你的代码。你应该会看到更高的对比度提升。 - rayryeng
啊,你说得对。我进行了归一化处理,这导致对比度减少太多了。谢谢! - mash
原因是,当您显示图像时,任何负值都将被截断为0。因此,请尝试在渐变中的每个像素上添加128,这样您就可以捕获负梯度。 - rayryeng
显示剩余2条评论
1个回答

3
我实际上已经从您创建的自定义内核中获得了输出。我使用Python OpenCV来完成这项工作,但调用OpenCV函数的方式基本相同。为了自包含,以下是我用Sobel和您的自定义内核调用图像的Python代码:
import cv2
import numpy as np
im = cv2.imread('Nj9fM.png'); #// Save image to computer first

#// Call using built-in Sobel
out1 = cv2.Sobel(im, cv2.CV_16S, 0, 1, 3)
out1 = cv2.convertScaleAbs(out1.copy())

#// Create custom kernel
xVals = np.array([0.125,0,-0.125,0.25,0,-0.25,0.125,0,-0.125]).reshape(3,3)

#// Call filter2D
out2 = cv2.filter2D(im, cv2.CV_32F, xVals, None, (-1,-1), 0, cv2.BORDER_DEFAULT)
out2 = cv2.convertScaleAbs(out2.copy())

cv2.imshow('Output 1', out1)
cv2.imshow('Output 2', out2)
cv2.waitKey(0)
cv2.destroyAllWindows()

在C ++方面:
#include <opencv2/opencv.hpp>
using namespace cv;

int main(int argc, char* argv[])
{
    Mat im = imread("Nj9fM.png", CV_LOAD_IMAGE_COLOR); // Save image to computer first

    // Call using built-in Sobel
    Mat out1, out2;
    Sobel(img, out1, CV_16S, 1, 0, 3);
    convertScaleAbs(out1, out2);

    // Create custom kernel
    Mat xVals = Mat_<float>(3, 3) << 0.125, 0, -0.125, 0.25, 0, -0.25, 0.125, 0, -0.125;

    // Call filter2D
    filter2D(im, out2, -1, xVals, Point(-1,-1), 0 ,BORDER_DEFAULT);
    convertScaleAbs(out2, out2);

    imshow("Output 1", out1);
    imshow("Output 2", out1);
    waitKey(0);
    destroyWindow("Output 1");
    destroyWindow("Output 2");
}

如果您运行此代码,您将实际上看到两个图像,其中第一个使用内置的Sobel算子,而另一个使用您的自定义卷积核。我看到Sobel实现和您的梯度之间的主要区别在于,您通过将每个元素除以8来获取Sobel卷积核。因此,您检测到的任何梯度都会得到对比度降低,这就是我所看到的。实际上,您基本上是取梯度结果并除以8,因此您正在将输出的强度减少了8倍。尝试使用以下代码:float xVals2[9] = {1f,0f,-1f,2f,0f,-2f,1f,0f,-1f}; 实际的Sobel卷积核,然后再次运行您的代码。您应该会看到更高的对比度增强。对于Python,可以这样做:
xVals2 = np.array([1.,0.,-1.,2.,0,-2.,1.,0,-1.]).reshape(3,3)

同时在C++中:

Mat xVals2 = Mat_<float>(3, 3) << 1f, 0f, -1f, 2f, 0f, -2f, 1f, 0f, -1f;

如果您使用这个内核来运行代码,您会发现对比度更高了。但是你会注意到有更多的噪声,因为梯度值更大了。


通过将Sobel核除以8,您可以获得正确的导数估计。 - Cris Luengo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接