【问题标题】:Sobel derivative in OpenCVOpenCV 中的 Sobel 导数
【发布时间】:2016-01-03 12:30:24
【问题描述】:

我的任务是制作自己的 Sobel 方法,而不是使用 OpenCV 中的 cv::Sobel。 我尝试实现我在Programming techniques 找到的一个

但是,当我运行程序时,cv::Mat 会引发错误。有人知道为什么吗?

索贝尔法:

int sobelCorrelation(Mat InputArray, int x, int y, String xory)
{
    if (xory == "x") {
        return InputArray.at<uchar>(y - 1, x - 1) +
            2 * InputArray.at<uchar>(y, x - 1) +
            InputArray.at<uchar>(y + 1, x - 1) -
            InputArray.at<uchar>(y - 1, x + 1) -
            2 * InputArray.at<uchar>(y, x + 1) -
            InputArray.at<uchar>(y + 1, x + 1);
    }
    else if (xory == "y")
    {
        return InputArray.at<uchar>(y - 1, x - 1) +
            2 * InputArray.at<uchar>(y - 1, x) +
            InputArray.at<uchar>(y - 1, x + 1) -
            InputArray.at<uchar>(y + 1, x - 1) -
            2 * InputArray.at<uchar>(y + 1, x) -
            InputArray.at<uchar>(y + 1, x + 1);
    }
    else
    {
        return 0;
    }
}

在另一个函数中调用和处理它:

void imageOutput(Mat image, String path) {
    image = imread(path, 0);
    Mat dst;
    dst = image.clone();
    int sum, gx, gy;
    if (image.data && !image.empty()){

        for (int y = 0; y < image.rows; y++)
            for (int x = 0; x < image.cols; x++)
                dst.at<uchar>(y, x) = 0.0;

        for (int y = 1; y < image.rows - 1; ++y) {
            for (int x = 1; x < image.cols - 1; ++x){ 
                gx = sobelCorrelation(image, x, y, "x");
                gy = sobelCorrelation(image, x, y, "y");
                sum = absVal(gx) + absVal(gy);
                if (sum > 255)
                    sum = 255;
                else if (sum < 0)
                    sum = 0;
                dst.at<uchar>(x, y) = sum;
            }
        }

        namedWindow("Original");
        imshow("Original", image);

        namedWindow("Diagonal Edges");
        imshow("Diagonal Edges", dst);

    }
    waitKey(0);
}

主要:

int main(int argc, char* argv[]) {

    Mat image;

    imageOutput(image, "C:/Dropbox/2-falling-toast-ted-kinsman.jpg");
    return 0;
}

absVal 方法:

int absVal(int v)
{
    return v*((v < 0)*(-1) + (v > 0));
}

运行时会抛出这个错误:

Unhandled exception at 0x00007FFC9365A1C8 in Miniproject01.exe: Microsoft C++ exception: cv::Exception at memory location 0x000000A780A4F110.

并指向这里:

template<typename _Tp> inline
_Tp& Mat::at(int i0, int i1)
{
    CV_DbgAssert( dims <= 2 && data && (unsigned)i0 < (unsigned)size.p[0] &&
        (unsigned)(i1 * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels()) &&
        CV_ELEM_SIZE1(DataType<_Tp>::depth) == elemSize1());
    return ((_Tp*)(data + step.p[0] * i0))[i1];
}

如果有人对我做错了什么有任何建议或想法,将不胜感激!

【问题讨论】:

  • dst.at&lt;uchar&gt;(x, y) = sum; 必须改为 dst.at&lt;uchar&gt;(y,x) = sum;。但是请注意,您的输出是int,但它会饱和到uchar,因此负值将变为零并且值> 255 将被截断。所以你可能不想做dst = image.clone();dst = cv::Mat(..., CV_32S) 并使用 dst.at 访问它
  • 是的,我自己发现的。大错特错!非常抱歉。
  • 查看更新的评论以获取更多提示可能会出错!
  • 我还想指出,这是非常缓慢的实现。你最好写一个卷积循环,并为 sobel X 和 Y 使用两个不同的内核
  • 感谢所有伟大的建议!三木,你有卷积循环的代码示例吗?

标签: c++ opencv sobel


【解决方案1】:

感谢您的帖子, 我能够使用上面的内核生成梯度图,并使用 openCV 代码 filter2D 从 Using custom kernel in opencv 2DFilter - causing crash ... convolution how?

将图像与内核进行卷积。我使用的代码是

    #include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv) {

    //Loading the source image
    Mat src;
    //src = imread("1.png");
    src = cv::imread("E:\\Gray_Image.bmp", 0);
    //Output image of the same size and the same number of channels as src.
    Mat dst1,dst2,grad;
    //Mat dst = src.clone();   //didn't help...

    //desired depth of the destination image
    //negative so dst will be the same as src.depth()
    int ddepth = -1;

    //the convolution kernel, a single-channel floating point matrix:


    //Mat kernel = imread("kernel.png");

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

    Mat kernel_y = (Mat_<float>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
    kernel_x.convertTo(kernel_x, CV_32F);  kernel_y.convertTo(kernel_y, CV_32F);   //<<not working
                                          //normalize(kernel, kernel, 1.0, 0.0, 4, -1, noArray());  //doesn't help

                                          //cout << kernel.size() << endl;  // ... gives 11, 11

                                          //however, the example from tutorial that does work:
                                          //kernel = Mat::ones( 11, 11, CV_32F )/ (float)(11*11);

                                          //default value (-1,-1) here means that the anchor is at the kernel center.
    Point anchor = Point(-1, -1);

    //value added to the filtered pixels before storing them in dst.
    double delta = 0;

    //alright, let's do this...
    filter2D(src, dst1, ddepth, kernel_x, anchor, delta, BORDER_DEFAULT);
    filter2D(src, dst2, ddepth, kernel_y, anchor, delta, BORDER_DEFAULT);

    imshow("Source", src);     //<< unhandled exception here
    //imshow("Kernel1", kernel_x);  imshow("Kernel2", kernel_y);
    imshow("Destination1", dst1);
    imshow("Destination2", dst2);
    addWeighted(dst1, 0.5, dst2, 0.5, 0, grad);
    imshow("Destination3", grad);
    waitKey(1000000);

    return 0;
}

【讨论】:

    【解决方案2】:

    如果我是你,我几乎总是会避免使用 for 循环(如果可能的话)。不必要的 for 循环往往会减慢执行速度。相反,尽可能重复使用。例如,下面的代码使用 filter2D 给出 2d Correlation 结果:

    Mat kern = (Mat_<float>(3,3)<<-1,0,1,-2,0,2,-1,0,1);
    Mat dest;
    cv::filter2D(src,dest,src.type(),kern);
    

    如果您想获得卷积结果,则需要在过滤之前翻转内核“kern”。

    cv::flip(kern,kern, -1);
    

    如果您想获得更多性能,可以使用可分离过滤器“sepFilter2D”。

    【讨论】:

      【解决方案3】:

      此代码 sn-p 用于演示如何计算 Sobel 3x3 导数,将图像与 Sobel 核进行卷积。您可以轻松扩展到不同的内核大小,将内核半径作为my_sobel 的输入,并创建适当的内核。

      #include <opencv2\opencv.hpp>
      #include <iostream>
      using namespace std;
      using namespace cv;
      
      
      void my_sobel(const Mat1b& src, Mat1s& dst, int direction)
      {
          Mat1s kernel;
          int radius = 0;
      
          // Create the kernel
          if (direction == 0)
          {
              // Sobel 3x3 X kernel
              kernel = (Mat1s(3,3) << -1, 0, +1, -2, 0, +2, -1, 0, +1);
              radius = 1;
          }
          else
          {
              // Sobel 3x3 Y kernel
              kernel = (Mat1s(3, 3) << -1, -2, -1, 0, 0, 0, +1, +2, +1);
              radius = 1;
          }
      
          // Handle border issues
          Mat1b _src;
          copyMakeBorder(src, _src, radius, radius, radius, radius, BORDER_REFLECT101);
      
          // Create output matrix
          dst.create(src.rows, src.cols);
      
          // Convolution loop
      
          // Iterate on image 
          for (int r = radius; r < _src.rows - radius; ++r)
          {
              for (int c = radius; c < _src.cols - radius; ++c)
              {
                  short s = 0;
      
                  // Iterate on kernel
                  for (int i = -radius; i <= radius; ++i)
                  {
                      for (int j = -radius; j <= radius; ++j)
                      {
                          s += _src(r + i, c + j) * kernel(i + radius, j + radius);
                      }
                  }
                  dst(r - radius, c - radius) = s;
              }
          }
      }
      
      int main(void)
      {
          Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
      
          // Compute custom Sobel 3x3 derivatives
          Mat1s sx, sy;
          my_sobel(img, sx, 0);
          my_sobel(img, sy, 1);
      
          // Edges L1 norm
          Mat1b edges_L1;
          absdiff(sx, sy, edges_L1);
      
      
          // Check results against OpenCV
          Mat1s cvsx,cvsy;
          Sobel(img, cvsx, CV_16S, 1, 0);
          Sobel(img, cvsy, CV_16S, 0, 1);
          Mat1b cvedges_L1;
          absdiff(cvsx, cvsy, cvedges_L1);
      
          Mat diff_L1;
          absdiff(edges_L1, cvedges_L1, diff_L1);
      
          cout << "Number of different pixels: " << countNonZero(diff_L1) << endl;
      
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-12
        • 2019-03-11
        • 1970-01-01
        • 2019-12-04
        • 2018-12-12
        • 1970-01-01
        相关资源
        最近更新 更多