需要帮助实现特殊的边缘检测器

时间:2010-12-24 14:34:40

标签: c++ image-processing opencv

我正在从研究论文中实施一种方法。该方法的一部分需要一个主要的边缘检测器,作者描述如下:

  1. 获取DC图像(宽度和高度均有效地缩减8)
  2. 计算DC图像的Sobel梯度
  3. 阈值Sobel梯度图像(使用T = 120)
  4. 清理边缘图像的形态操作
  5. 请注意, NOT Canny边缘检测 - 他们不会为非最大限制抑制等问题而烦恼。我当然可以使用Canny边缘检测执行此操作,但我想实现与论文中表达的完全一样。

    最后一步是我有点卡住了。

    以下是作者对此的评价:

      

    获得二进制文件后   边缘图从边缘检测过程,二元形态学   使用操作来移除孤立的边缘像素,   这可能会在边缘检测期间导致误报

    这就是事情在最后的结果看起来是怎么样的(边块被填充为黑色):

    alt text

    如果我跳过最后一步,这就是我所拥有的:

    alt text

    似乎走在正确的轨道上。所以,如果我对第4步进行侵蚀,会发生什么:

    alt text

    我已经尝试过侵蚀和扩张的组合以获得与它们相同的结果,但是没有得到任何接近。任何人都可以建议结合使用形态学运算符来获得理想的结果吗?

    以下是二值化输出,以防有人想玩它:

    alt text

    如果你真的很热衷,这里是源代码(C ++):

    #include <cv.h>
    #include <highgui.h>
    #include <stdlib.h>
    #include <assert.h>
    
    using cv::Mat;
    using cv::Size;
    
    #include <stdio.h>
    
    #define DCTSIZE 8
    #define EDGE_PX 255
    
    /*
     * Display a matrix as an image on the screen.
     */
    void
    show_mat(char *heading, Mat const &m)
    {
        Mat clone = m.clone();
    
        Mat scaled(clone.size(), CV_8UC1);
        convertScaleAbs(clone, scaled);
    
        IplImage ipl = scaled;
    
        cvNamedWindow(heading, CV_WINDOW_AUTOSIZE); 
        cvShowImage(heading, &ipl);
        cvWaitKey(0);
    }
    
    /*
     * Get the DC components of the specified matrix as an image.
     */
    Mat
    get_dc(Mat const &m)
    {
        Size s = m.size();
        assert(s.width  % DCTSIZE == 0);
        assert(s.height % DCTSIZE == 0);
    
        Size dc_size = Size(s.height/DCTSIZE, s.width/DCTSIZE);
    
        Mat dc(dc_size, CV_32FC1);
        cv::resize(m, dc, dc_size, 0, 0, cv::INTER_AREA);
    
        return dc;
    }
    
    /*
     * Detect the edges:
     *
     * Sobel operator
     * Thresholding
     * Morphological operations
     */
    Mat
    detect_edges(Mat const &src, int T)
    {
        Mat sobelx    = Mat(src.size(), CV_32FC1);
        Mat sobely    = Mat(src.size(), CV_32FC1);
        Mat sobel_sum = Mat(src.size(), CV_32FC1);
    
        cv::Sobel(src, sobelx, CV_32F, 1, 0, 3, 0.5);
        cv::Sobel(src, sobely, CV_32F, 0, 1, 3, 0.5);
    
        cv::add(cv::abs(sobelx), cv::abs(sobely), sobel_sum);
    
        Mat binarized = src.clone();
        cv::threshold(sobel_sum, binarized, T, EDGE_PX, cv::THRESH_BINARY);
    
        cv::imwrite("binarized.png", binarized);
    
        //
        // TODO: this is the part I'm having problems with.
        //
    
    #if 0
        //
        // Try a 3x3 cross structuring element.
        //
        Mat elt(3,3, CV_8UC1);
        elt.at<uchar>(0, 1) = 0;
        elt.at<uchar>(1, 0) = 0;
        elt.at<uchar>(1, 1) = 0;
        elt.at<uchar>(1, 2) = 0;
        elt.at<uchar>(2, 1) = 0;
    #endif
    
        Mat dilated = binarized.clone();
        //cv::dilate(binarized, dilated, Mat());
    
        cv::imwrite("dilated.png", dilated);
    
        Mat eroded = dilated.clone();
        cv::erode(dilated, eroded, Mat());
    
        cv::imwrite("eroded.png", eroded);
    
        return eroded;
    }
    
    /*
     * Black out the blocks in the image that contain DC edges.
     */
    void
    censure_edge_blocks(Mat &orig, Mat const &edges)
    {
        Size s = edges.size();
        for (int i = 0; i < s.height; ++i)
        for (int j = 0; j < s.width;  ++j)
        {
            if (edges.at<float>(i, j) != EDGE_PX)
                continue;
    
            int row = i*DCTSIZE;
            int col = j*DCTSIZE;
    
            for (int m = 0; m < DCTSIZE; ++m)
            for (int n = 0; n < DCTSIZE; ++n)
                orig.at<uchar>(row + m, col + n) = 0;
        }
    }
    
    /*
     * Load the image and return the first channel.
     */
    Mat
    load_grayscale(char *filename)
    {
        Mat orig = cv::imread(filename);
        std::vector<Mat> channels(orig.channels());
        cv::split(orig, channels);
        Mat grey = channels[0];
        return grey;
    }
    
    int
    main(int argc, char **argv)
    {
        assert(argc == 3);
    
        int bin_thres = atoi(argv[2]);
    
        Mat orig = load_grayscale(argv[1]);
        //show_mat("orig", orig);
    
        Mat dc = get_dc(orig);
        cv::imwrite("dc.png", dc);
    
        Mat dc_edges = detect_edges(dc, bin_thres);
    
        cv::imwrite("dc_edges.png", dc_edges);
    
        censure_edge_blocks(orig, dc_edges);
        show_mat("censured", orig);
        cv::imwrite("censured.png", orig);
    
        return 0;
    }
    

4 个答案:

答案 0 :(得分:2)

我无法想象任何形态运算的组合会产生与所谓的正确结果所检测到的相同的边缘,给定您的部分结果作为输入。

我注意到底层图像不同;这可能会导致您的结果如此不同。 Lena图像适用于指示结果的类型,但不能用于比较。你有与原作者完全相同的形象吗?

答案 1 :(得分:1)

作者描述的内容可以通过连接组件分析实现,使用8way连接。我不会称之为形态学。

我确实认为你遗漏了别的东西:他们的图像边缘不比一个像素厚。你的。您引用的段落仅涉及删除孤立的像素,因此必须有一个您错过或以不同方式实施的步骤。

祝你好运!

答案 2 :(得分:0)

我认为你所需要的是一种侵蚀或开放,从某种意义上说,它是4向而不是8向。 OpenCV的默认形态内核是一个3x3矩形(IplConvKernelshape = CV_SHAPE_RECT)。这对于薄边缘来说非常苛刻。

您可能希望尝试使用IplConvKernel = shape的3x3自定义CV_SHAPE_CROSS进行侵蚀。 如果你需要一个更精细的过滤器,你可能想尝试用4个不同的CV_SHAPE_RECT大小为1x2的内核,2x1和每个的(0,1)和(1,0)中的锚点进行侵蚀。

答案 3 :(得分:0)

首先,您的输入图像具有更高的分辨率,测试输入图像可以解释检测到更少边缘的事实 - 更改更加平滑。

其次,由于边缘被阈值化为0,因此尝试在较小的邻域上进行扩张(例如,将每个像素与4个原始邻居进行比较(以非串行方式))以消除孤立的边缘。