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

mpe*_*kov 7 c++ opencv image-processing

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

  1. 获得DC图像(宽度和高度均有效地缩减8)
  2. 计算DC图像的Sobel梯度
  3. 阈值索贝尔梯度图像(使用T = 120)
  4. 用于清理边缘图像的形态学操作

请注意,这不是 Canny边缘检测 - 它们不会对非最大限制抑制等问题感到困扰.我当然可以使用Canny边缘检测来执行此操作,但我希望完全按照本文中的表达方式实现.

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

以下是作者对此的评价:

在从边缘检测过程获得二值边缘图之后,采用二元形态学运算来去除孤立的边缘像素,这可能在边缘检测期间引起误报警.

这就是事情在最后的结果应该是什么样的(边块被填充为黑色):

替代文字

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

替代文字

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

替代文字

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

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

替代文字

如果你真的很热衷,这里是源代码(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;
}
Run Code Online (Sandbox Code Playgroud)

koa*_*oan 2

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

我注意到底层图像是不同的;这可能就是你的结果如此不同的原因。Lena 图像适合指示结果类型,但不适合比较。你有和原作者一模一样的图片吗?