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

Posted

技术标签:

【中文标题】opencv Sobel算子是不是计算相关性?【英文标题】:Does opencv Sobel operator compute correlation?opencv Sobel算子是否计算相关性? 【发布时间】:2017-06-21 13:58:10 【问题描述】:

opencv Sobel 算子是否计算相关性,而不是卷积? 我知道 filter2D 函数计算相关性,我刚刚发现(根据代码的结果)Sobel 算子也计算相关性。这是预期的吗?我在这里错过了什么吗?

Mat sobelx, dest1,dest2;
Sobel(src, sobelx, src.depth(),1,0,3);
imshow("Sobel filtered in x dir" ,sobelx);
Mat kern = (Mat_<float>(3,3)<<-1,0,1,-1,0,1,-1,0,1);
filter2D(src,dest1,src.type(),kern);
imshow("Sobel in x-dir using corr ", dest1);
/* dest1 equals sobelx  */
flip(kern,kern, -1);//flip the kernel in x and y direction for convolution
filter2D(src,dest2,src.type(),kern);
imshow("Sobel in x-dir using conv ",dest2,);

【问题讨论】:

【参考方案1】:

这是我编写的测试,输出说明了事实。 cv::Sobel 计算相关性而不是卷积。 我使用了以下内核,它与 cv::Sobel 使用的内核相同

kern = [-1, 0, 1;
        -2, 0, 2;
        -1, 0, 1]

结果表明sobelx和filter2d(correlation)的输出是一样的。

void test_sobel()
cv::RNG(0);
cv::Mat src(4,4, CV_8U);
cv::randu(src, 0, 256);
cv::Mat sobelx, dest_corr,dest_conv;
cv::Sobel(src, sobelx, CV_32F,1,0,3);
// sobel uses a 3x3 filter shown as below
Mat kern = (Mat_<float>(3,3)<<-1,0,1,-2,0,2,-1,0,1);
// filter2D computes correlation
cv::filter2D(src,dest_corr,CV_32F,kern);

//flip the kernel in x and y direction for convolution
cv::Mat kern_conv;
cv::flip(kern,kern_conv, -1);
cv::filter2D(src,dest_conv,CV_32F,kern_conv);

std::cout << "kern = \n" << kern<< std::endl;
std::cout << "kern_conv = \n" << kern_conv<< std::endl;
std::cout << "src = \n" << src << std::endl;
std::cout << "sobelx = \n" << sobelx<< std::endl;
std::cout << "dest_corr = \n" << dest_corr<< std::endl;
std::cout << "dest_conv =\n" << dest_conv << std::endl;
cv::Mat diff1 = sobelx - dest_corr;
cv::Mat diff2 = sobelx - dest_conv;
std::cout << "sobelx - dest_corr = " << cv::sum( diff1 )[0] << std::endl;
std::cout << "sobelx - dest_conv = " << cv::sum( diff2 )[0] << std::endl;

输出:

kern = 
[-1, 0, 1;
 -2, 0, 2;
-1, 0, 1]
kern_conv = 
[1, 0, -1;
 2, 0, -2;
 1, 0, -1]
src = 
[246, 156, 192,   7;
 165, 166,   2, 179;
 231, 212, 171, 230;
  93, 138, 123,  80]
sobelx = 
[0, -434, -272, 0;
 0, -440, -105, 0;
 0, -253, -9, 0;
 0, -60, -80, 0]
dest_corr = 
[0, -434, -272, 0;
 0, -440, -105, 0;
 0, -253, -9, 0;
 0, -60, -80, 0]
dest_conv =
[0, 434, 272, 0;
 0, 440, 105, 0;
 0, 253, 9, 0;
 0, 60, 80, 0]
sobelx - dest_corr = 0
sobelx - dest_conv = -3306

【讨论】:

【参考方案2】:

编辑:

要获取 OpenCV Sobel 内核,您可以调用 cv::getDerivKernels 来获取两个可分离的内核(实际上在 cv::Sobel 的 OpenCV 代码中调用的是 cv::sepFilter2D,或者 Intel IPP Sobel 或任何其他后端如果存在)并且确实执行了相关性。

下面提到的内核已经过时,但测试代码仍然有用!


最好的选择是测试以避免意外:


    cv::RNG(0); //fix RNG

    cv::Mat img(8, 8, CV_8U);
    cv::randu(img, 0, 256);

    cv::Mat sobel_x;
    cv::Sobel(img, sobel_x, CV_64F, 1, 0);

    cv::Mat sobel_filter;
    cv::Mat kern = (cv::Mat_<double>(3, 3) << 1, 0, -1,
                                              2, 0, -2,
                                              1, 0, -1);
    cv::filter2D(img, sobel_filter, CV_64F, kern);

    cv::Mat kern_flip, sobel_filter_conv;
    cv::flip(kern, kern_flip, -1);
    cv::filter2D(img, sobel_filter_conv, CV_64F, kern_flip);

    std::cout << "img:\n" << img << std::endl;
    std::cout << "sobel_x:\n" << sobel_x << std::endl;
    std::cout << "sobel_filter:\n" << sobel_filter << std::endl;
    std::cout << "sobel_filter_conv:\n" << sobel_filter_conv << std::endl;

    cv::Mat diff = sobel_x - sobel_filter;
    cv::Mat diff2 = sobel_x - sobel_filter_conv;
    std::cout << "Diff sobel_x - sobel_filter: " << cv::sum( diff )[0] << std::endl;
    std::cout << "Diff sobel_x - sobel_filter_conv: " << cv::sum( diff2 )[0] << std::endl;




    cv::RNG(0); //fix RNG

    cv::Mat img(8, 8, CV_8U);
    cv::randu(img, 0, 256);

    cv::Mat sobel_y;
    cv::Sobel(img, sobel_y, CV_64F, 0, 1);

    cv::Mat sobel_filter;
    cv::Mat kern = (cv::Mat_<double>(3, 3) << 1,  2,  1,
                                              0,  0,  0,
                                             -1, -2, -1);
    cv::filter2D(img, sobel_filter, CV_64F, kern);

    cv::Mat kern_flip, sobel_filter_conv;
    cv::flip(kern, kern_flip, -1);
    cv::filter2D(img, sobel_filter_conv, CV_64F, kern_flip);

    std::cout << "img:\n" << img << std::endl;
    std::cout << "sobel_y:\n" << sobel_y << std::endl;
    std::cout << "sobel_filter:\n" << sobel_filter << std::endl;
    std::cout << "sobel_filter_conv:\n" << sobel_filter_conv << std::endl;

    cv::Mat diff = sobel_y - sobel_filter;
    cv::Mat diff2 = sobel_y - sobel_filter_conv;
    std::cout << "Diff sobel_y - sobel_filter: " << cv::sum( diff )[0] << std::endl;
    std::cout << "Diff sobel_y - sobel_filter_conv: " << cv::sum( diff2 )[0] << std::endl;

输出:

img:
[246, 156, 192,   7, 165, 166,   2, 179;
 231, 212, 171, 230,  93, 138, 123,  80;
 105, 242, 231, 239, 174, 174, 176, 191;
 134,  54, 234,  69,  25, 147,  24,  67;
 124, 158, 203, 206,  89, 144, 210,  51;
  31, 132, 123, 250, 234, 246, 204,  74;
 111, 208, 249, 149, 234,  37,  55, 147;
 143,  29, 214, 169, 215,  84, 190, 204]
sobel_x:
[0, -228, -262, -210, 134, -266, -90, 0;
 0, -48, -116, -240, -90, -101, -86, 0;
 0, 292, 27, -401, -144, 33, -104, 0;
 0, 405, 75, -589, 29, 121, -236, 0;
 0, 350, 229, -326, -50, 211, -438, 0;
 0, 401, 225, 93, -182, -118, -327, 0;
 0, 439, 140, 82, -313, -413, 168, 0;
 0, 418, 162, -28, -394, -408, 460, 0]
sobel_filter:
[0, 228, 262, 210, -134, 266, 90, 0;
 0, 48, 116, 240, 90, 101, 86, 0;
 0, -292, -27, 401, 144, -33, 104, 0;
 0, -405, -75, 589, -29, -121, 236, 0;
 0, -350, -229, 326, 50, -211, 438, 0;
 0, -401, -225, -93, 182, 118, 327, 0;
 0, -439, -140, -82, 313, 413, -168, 0;
 0, -418, -162, 28, 394, 408, -460, 0]
sobel_filter_conv:
[0, -228, -262, -210, 134, -266, -90, 0;
 0, -48, -116, -240, -90, -101, -86, 0;
 0, 292, 27, -401, -144, 33, -104, 0;
 0, 405, 75, -589, 29, 121, -236, 0;
 0, 350, 229, -326, -50, 211, -438, 0;
 0, 401, 225, 93, -182, -118, -327, 0;
 0, 439, 140, 82, -313, -413, 168, 0;
 0, 418, 162, -28, -394, -408, 460, 0]
Diff sobel_x - sobel_filter: -3428
Diff sobel_x - sobel_filter_conv: 0

img:
[110, 239, 216, 103, 137, 248, 173,  53;
 221, 142,  83, 166, 251, 222, 243,  67;
  29, 232, 208, 159, 115,  91, 244, 128;
 151, 254,  47,  13, 132, 253, 137, 127;
 180, 181, 179, 168, 236, 246,  66, 169;
 188, 198, 198, 137, 131,  63,   5, 237;
 220, 209, 155,  88,  28,  12,  58,  99;
   6,  49, 196, 195, 163,   9,  82, 197]
sobel_y:
[0, 0, 0, 0, 0, 0, 0, 0;
 -176, -103, 33, 82, -145, -265, 60, 292;
 84, 118, -113, -461, -360, -163, -121, -92;
 200, 20, -100, 110, 406, 253, -160, -274;
 -38, 76, 370, 398, -68, -513, -344, -44;
 136, 72, -100, -392, -730, -684, -320, -156;
 -662, -482, -95, 146, 68, 1, 60, 74;
 0, 0, 0, 0, 0, 0, 0, 0]
sobel_filter:
[0, 0, 0, 0, 0, 0, 0, 0;
 176, 103, -33, -82, 145, 265, -60, -292;
 -84, -118, 113, 461, 360, 163, 121, 92;
 -200, -20, 100, -110, -406, -253, 160, 274;
 38, -76, -370, -398, 68, 513, 344, 44;
 -136, -72, 100, 392, 730, 684, 320, 156;
 662, 482, 95, -146, -68, -1, -60, -74;
 0, 0, 0, 0, 0, 0, 0, 0]
sobel_filter_conv:
[0, 0, 0, 0, 0, 0, 0, 0;
 -176, -103, 33, 82, -145, -265, 60, 292;
 84, 118, -113, -461, -360, -163, -121, -92;
 200, 20, -100, 110, 406, 253, -160, -274;
 -38, 76, 370, 398, -68, -513, -344, -44;
 136, 72, -100, -392, -730, -684, -320, -156;
 -662, -482, -95, 146, 68, 1, 60, 74;
 0, 0, 0, 0, 0, 0, 0, 0]
Diff sobel_y - sobel_filter: -8204
Diff sobel_y - sobel_filter_conv: 0

【讨论】:

嗨@Catree,您的测试再次证明 cv::Sobel 计算的是相关性而不是卷积。您在代码 sn-p 中使用的 kern 已经翻转。因此结果相反。使用以下内核,您将获得实际结果。 cv::Mat kern = (cv::Mat_(3, 3) docs.opencv.org/3.0.0/d2/d2c/tutorial_sobel_derivatives.html] 我找不到关于 Sobel 内核的共识(例如,这些来源给出的形式略有不同:en.wikipedia.org/wiki/Sobel_operator;cim.mcgill.ca/~image529/TA529/Image529_99/assignments/…;***.com/questions/9567882/…)。出于好奇,为什么它对您很重要? 其他来源:software.intel.com/en-us/node/504204?language=en; github.com/scikit-image/scikit-image/blob/master/skimage/… 在我看来,如果您需要准确的结果,只需使用 cv::filter2D 和您选择的内核即可。 sobel 肯定有不同的风味,都用于检测边缘。但是,我们将 cv::sobel 与 cv::filter2D 进行比较,opencv 文档提到它使用了我之前提到的那个。 docs.opencv.org/3.0.0/d2/d2c/tutorial_sobel_derivatives.html 这对我很重要,因为我的最终目标是计算梯度方向并在之后使用它。 不得不提这篇论文:An Isotropic 3x3 Image Gradient Operator (researchgate.net/publication/…) 用于Sobel内核的定义。您会观察到几乎每个源都有一个稍微不同的内核。所以,我改变了一点我的答案。

以上是关于opencv Sobel算子是不是计算相关性?的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV入门系列4:图像梯度常用算子——Sobel,Scharr和Laplacian算子

opencv用sobel算子进行处理后的图像,怎么提取梯度信息?

sobel算子原理与实现

Opencv图像边缘检测——Roberts算子(手写)Sobel算子(手写和调包)Scharr算子Laplacian算子

opencv-python图像处理 ----图像梯度Sobel算子

opencv入门之九Opencv边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器