OpenCV Sobel函数的手动实现

3 投票
1 回答
4212 浏览
提问于 2025-04-18 17:50

My image

我有一张图片,使用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 个回答

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实现和你的梯度之间的主要区别是,你把Sobel内核的每个元素都除以了8。因此,你检测到的任何梯度都会导致对比度降低,这就是我看到的效果。实际上,你基本上是把梯度结果除以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;

如果你使用这个内核运行你的代码,你会发现对比度更高了。不过,你会看到噪声也更多,因为梯度值变大了。

撰写回答