Need help implementing a special edge detector <p>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:</p> <ol> <li>Obtain DC image (effectively downsample by 8 for both width and height)</li> <li>Calculate Sobel gradient of DC image</li> <li>Threshold Sobel gradient image (using T=120)</li> <li><strong>Morphological operations to clean up edge image</strong></li> </ol> <p>Note that this <strong>NOT</strong> 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.</p> <p>That last step is the one I'm a bit stuck on.</p> <p>Here is exactly what the authors say about it:</p> <blockquote> <p>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</p> </blockquote> <p>Here's how things are supposed to look like at the end of it all (edge blocks have been filled in black):</p> <p><img src="https://i.stack.imgur.com/xDpni.png" alt="alt text"></p> <p>Here's what I have if I skip the last step:</p> <p><img src="https://i.stack.imgur.com/RpA6R.png" alt="alt text"></p> <p>It seems to be on the right track. So here's what happens if I do erosion for step 4:</p> <p><img src="https://i.stack.imgur.com/bKB55.png" alt="alt text"></p> <p>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?</p> <p>Here's the binarization output, in case anyone wants to play around with it:</p> <p><img src="https://i.stack.imgur.com/nX5J3.png" alt="alt text"></p> <p>And if you're really keen, here is the source code (C++):</p> <pre><code>#include &lt;cv.h&gt; #include &lt;highgui.h&gt; #include &lt;stdlib.h&gt; #include &lt;assert.h&gt; using cv::Mat; using cv::Size; #include &lt;stdio.h&gt; #define DCTSIZE 8 #define EDGE_PX 255 /* * Display a matrix as an image on the screen. */ void show_mat(char *heading, Mat const &amp;m) { Mat clone = m.clone(); Mat scaled(clone.size(), CV_8UC1); convertScaleAbs(clone, scaled); IplImage ipl = scaled; cvNamedWindow(heading, CV_WINDOW_AUTOSIZE); cvShowImage(heading, &amp;ipl); cvWaitKey(0); } /* * Get the DC components of the specified matrix as an image. */ Mat get_dc(Mat const &amp;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 &amp;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&lt;uchar&gt;(0, 1) = 0; elt.at&lt;uchar&gt;(1, 0) = 0; elt.at&lt;uchar&gt;(1, 1) = 0; elt.at&lt;uchar&gt;(1, 2) = 0; elt.at&lt;uchar&gt;(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 &amp;orig, Mat const &amp;edges) { Size s = edges.size(); for (int i = 0; i &lt; s.height; ++i) for (int j = 0; j &lt; s.width; ++j) { if (edges.at&lt;float&gt;(i, j) != EDGE_PX) continue; int row = i*DCTSIZE; int col = j*DCTSIZE; for (int m = 0; m &lt; DCTSIZE; ++m) for (int n = 0; n &lt; DCTSIZE; ++n) orig.at&lt;uchar&gt;(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&lt;Mat&gt; 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; } </code></pre>