Need help implementing a special edge detector

I'm implementing an approach from a research paper. Part of the approach calls for a major edge detector, which the authors describe as follows:

  • Obtain DC image (effectively downsample by 8 for both width and height)
  • Calculate Sobel gradient of DC image
  • Threshold Sobel gradient image (using T=120)
  • Morphological operations to clean up edge image
  • Note that this NOT Canny edge detection -- they don't bother with things like non-maximum suppression, etc. I could of course do this with Canny edge detection, but I want to implement things exactly as they are expressed in the paper.

    That last step is the one I'm a bit stuck on.

    Here is exactly what the authors say about it:

    After obtaining the binary edge map from the edge detection process, a binary morphological operation is employed to remove isolated edge pixels, which might cause false alarms during the edge detection

    Here's how things are supposed to look like at the end of it all (edge blocks have been filled in black):

    替代文字

    Here's what I have if I skip the last step:

    替代文字

    It seems to be on the right track. So here's what happens if I do erosion for step 4:

    替代文字

    I've tried combinations of erosion and dilation to obtain the same result as they do, but don't get anywhere close. Can anyone suggest a combination of morphological operators that will get me the desired result?

    Here's the binarization output, in case anyone wants to play around with it:

    And if you're really keen, here is the source code (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;
    }
    

    I can't imagine any combination of morphological operations that would produce the same edges as detected by the supposedly correct result, given your partial result as input.

    I note that the underlying image is different; this probably contributes to why your results are so different. The Lena image is fine for indicating the type of result but not for comparisons. Do you have the exact same image as the original authors ?


    What the authors described could be implemented with connected component analysis, using 8way connectivity. I would not call that morphological though.

    I do think you are missing something else: Their image does not have edges that are thicker than one pixel. Yours has. The paragraph you quoted only talks about removing isolated pixels, so there must be a step you missed or implemented differently.

    Good luck!


    I think that what you need is a kind of erode or open that is, in a sense, 4-way and not 8-way. The default morphological kernel for OpenCV is a 3x3 rectangle ( IplConvKernel with shape = CV_SHAPE_RECT ). This is pretty harsh on thin edges.

    You might want to try eroding with a 3x3 custom IplConvKernel with shape = CV_SHAPE_CROSS . If you need an even finer filter, you may want to try eroding with 4 different CV_SHAPE_RECT kernels of size 1x2, 2x1 with the anchor in (0,1) and (1,0) for each.

    链接地址: http://www.djcxy.com/p/4056.html

    上一篇: 如何从ASP.NET标记获取资源字符串?

    下一篇: 需要帮助实施特殊的边缘检测器