Smoothing Edges of a Binary Image

28,816

Solution 1

Here is the result I obtained with your image: enter image description here

My method is mostly based on several cv::medianBlurapplied on a scaled-up image.

Here is the code:

cv::Mat vesselImage = cv::imread(filename); //the original image
cv::threshold(vesselImage, vesselImage, 125, 255, THRESH_BINARY);
cv::Mat blurredImage; //output of the algorithm
cv::pyrUp(vesselImage, blurredImage);

for (int i = 0; i < 15; i++)
    cv::medianBlur(blurredImage, blurredImage, 7);

cv::pyrDown(blurredImage, blurredImage);
cv::threshold(blurredImage, blurredImage, 200, 255, THRESH_BINARY);

The jagged edges are due to the thresholding. If you are comfortable with an output image that is non-binary (i.e. with 256 shades of grAy), you can just remove it and you get this image: enter image description here

Solution 2

You can dilate then erode the areas http://docs.opencv.org/2.4/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html.

import cv2
import numpy as np
blur=((3,3),1)
erode_=(5,5)
dilate_=(3, 3)
cv2.imwrite('imgBool_erode_dilated_blured.png',cv2.dilate(cv2.erode(cv2.GaussianBlur(cv2.imread('so-br-in.png',0)/255, blur[0], blur[1]), np.ones(erode_)), np.ones(dilate_))*255)  

From To

EDIT whith a scale facor off 4 before the stuffenter image description here

Solution 3

i made some modifications on @dhanushka 's answer for another question and get these images.

Sorry it is C++ code but maybe you will convert it to Python.

enter image description here

You can change the parameters below to get different results.

// contour smoothing parameters for gaussian filter
int filterRadius = 10; // you can try to change this value
int filterSize = 2 * filterRadius + 1;
double sigma = 20; // you can try to change this value

enter image description here

#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main( int argc, const char** argv )
{
    Mat im = imread(argv[1], 0);

    Mat cont = ~im;
    Mat original = Mat::zeros(im.rows, im.cols, CV_8UC3);
    Mat smoothed = Mat(im.rows, im.cols, CV_8UC3, Scalar(255,255,255));

    // contour smoothing parameters for gaussian filter
    int filterRadius = 5;
    int filterSize = 2 * filterRadius + 1;
    double sigma = 10;

    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    // find contours and store all contour points
    findContours(cont, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, Point(0, 0));
    for(size_t j = 0; j < contours.size(); j++)
    {
        // extract x and y coordinates of points. we'll consider these as 1-D signals
        // add circular padding to 1-D signals
        size_t len = contours[j].size() + 2 * filterRadius;
        size_t idx = (contours[j].size() - filterRadius);
        vector<float> x, y;
        for (size_t i = 0; i < len; i++)
        {
            x.push_back(contours[j][(idx + i) % contours[j].size()].x);
            y.push_back(contours[j][(idx + i) % contours[j].size()].y);
        }
        // filter 1-D signals
        vector<float> xFilt, yFilt;
        GaussianBlur(x, xFilt, Size(filterSize, filterSize), sigma, sigma);
        GaussianBlur(y, yFilt, Size(filterSize, filterSize), sigma, sigma);
        // build smoothed contour
        vector<vector<Point> > smoothContours;
        vector<Point> smooth;
        for (size_t i = filterRadius; i < contours[j].size() + filterRadius; i++)
        {
            smooth.push_back(Point(xFilt[i], yFilt[i]));
        }
        smoothContours.push_back(smooth);

        Scalar color;

        if(hierarchy[j][3] < 0 )
        {
            color = Scalar(0,0,0);
        }
        else
        {
            color = Scalar(255,255,255);
        }
        drawContours(smoothed, smoothContours, 0, color, -1);
    }
    imshow( "result", smoothed );
    waitKey(0);
}

Solution 4

What you can do is increase the resolution of your image (e.g. double or triple it using resize). After that, erosion and dilation as described in the other answer above will lead to finer results.

Solution 5

You most probably got a gray scale image of the blood vessels first and then thresholded. It still looks non-smooth because the original gray scale image had noise inside. Asking now for a smoothing of the edges will result in lower resolution. For example the dilution and erosion proposed in another answer might fuse neighboring vessels in the dilution step which then cannot be separated again in the erosion step.

It might be preferable to remove the noise in the gray scale image first (aka do a smoothing there) and do the thresholding as the last step.

Because you did not deliver the gray scale image I performed a mild smoothing (about one pixel width) here on the binary image and performed a thresholding again.

enter image description here

I did a smoothing (with a Gaussian kernel of fixed size) and a thresholding (with a thresholding parameter). I suggest you do that on the grayscale image data and adjust the two parameters until you like the result.

Matlab code in case it is of interest:

% read
img = imread('YyNQV.png');
img = double(img(:, :, 1) ~= 255); % png is RGB -> binary

% smooth
kernel = fspecial('gaussian', 10, 1.5);
kernel = kernel / sum(kernel(:)); % normalize to 1
img_smooth = conv2(img, kernel, 'same');

% binarize again
threshold = 0.4; % experiment with values between 0 and 1
img_smooth_threshold = img_smooth > threshold;

% save (exchange black and white)
imwrite(~img_smooth_threshold, 'YyNQV_smooth.png');
Share:
28,816
Abdul Fatir
Author by

Abdul Fatir

Updated on October 15, 2021

Comments

  • Abdul Fatir
    Abdul Fatir over 2 years

    How to smooth the edges of this binary image of blood vessels obtained after thresholding.

    enter image description here

    I tried a method somewhat similar to this method but did not quite get the result I expected.

    enter image description here

    Here's the code:

    import cv2
    import numpy as np
    
    INPUT = cv2.imread('so-br-in.png',0)
    MASK = np.array(INPUT/255.0, dtype='float32')
    
    MASK = cv2.GaussianBlur(MASK, (5,5), 11)
    BG = np.ones([INPUT.shape[0], INPUT.shape[1], 1], dtype='uint8')*255
    
    OUT_F = np.ones([INPUT.shape[0], INPUT.shape[1], 1],dtype='uint8')
    
    for r in range(INPUT.shape[0]):
        for c in range(INPUT.shape[1]):
            OUT_F[r][c]  = int(BG[r][c]*(MASK[r][c]) + INPUT[r][c]*(1-MASK[r][c]))
    
    cv2.imwrite('brain-out.png', OUT_F)  
    

    What can be done to improve the smoothing of these harsh edges?

    EDIT

    I'd like to smoothen the edges something like http://pscs5.tumblr.com/post/60284570543. How to do this in OpenCV?