OpenCV Sobel函数的手动实现

Posted

技术标签:

【中文标题】OpenCV Sobel函数的手动实现【英文标题】:Manual implementation of OpenCV Sobel function 【发布时间】:2014-08-19 20:20:55 【问题描述】:

如果我有图像并使用

调用 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);

生成的渐变图像全黑。任何想法我做错了什么? 谢谢

【问题讨论】:

使用您的自定义内核,我得到一个输出。你能把你使用的图片放在这里吗?但是,我使用的是 Python OpenCV,但调用函数的方式实际上与 C++ 版本相同。我使用了我拥有的测试图像,并从 Sobel 和您的自定义内核中获得了输出。 有趣的是,我没有得到自定义内核的任何东西。我已经编辑了我的帖子以包含我使用的图片。 我现在去看看。给我一点时间。 我看到的 Sobel 实现和您的梯度之间的主要区别在于,您将 Sobel 核除以每个元素除以 8。因此,您检测到的任何梯度,您都会得到对比减少,这就是我所看到的。实际上,您基本上是在获取梯度结果并除以 8,因此您将输出强度降低了 8 倍。尝试执行以下操作:float xVals[9] = 1f,0f,-1f,2f,0f,-2f,1f,0f,-1f; actual Sobel 内核,然后运行您的再次编码。您应该会看到更高的对比度提升。 啊,你是对的。我正在规范化,这导致对比度降低了太多。谢谢! 【参考方案1】:

我实际上从您创建的自定义内核中获得了输出。我使用 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,您可以得到正确的导数估计。

以上是关于OpenCV Sobel函数的手动实现的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV Sobel()函数

《OpenCV:Sobel算子理论与OpenCV代码实现》

sobel函数

OpenCV——Sobel边缘检测

phase函数——opencv

opencv Sobel算子是不是计算相关性?