Ask Your Question

Revision history [back]

Here is a Sample implementation of Contour based and Image based edge-smoothing algorithms! Hope this helps!

#include <iostream>
#include "opencv2/opencv.hpp"

#include <iostream>

using namespace cv;
using namespace std;

bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) ;

bool SmoothEdge( Mat mInput_Bgr,Mat &mOutput_Bgr, double amount, double radius, uchar Threshold) 
{
    if(mInput_Bgr.empty())
    {
        return 0;
    }

    if(radius<1)
        radius=1;

    Mat mInput,mOutput;
    Mat mChannel[3];

    split(mInput_Bgr,mChannel);

    for (int i = 0; i < 3; i++)
    {
        mInput= mChannel[i];
        SmoothEdgeSingleChannel(mInput,mOutput,amount, radius,Threshold); 
        mOutput.copyTo(mChannel[i]);
    }
    merge(mChannel,3,mOutput_Bgr);


    return true;
}

bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) 
{
    if(mInput.empty())
    {
        return 0;
    }
    if(radius<1)
        radius=1;

    Mat mGSmooth,mDiff,mAbsDiff;
    mOutput = Mat(mInput.size(),mInput.type());

    GaussianBlur(mInput,mGSmooth,Size(0,0),radius); 
    //imshow("mGSmooth",mGSmooth);

    subtract(mGSmooth,mInput,mDiff);
    //imshow("mDiff",mDiff);

    mDiff*=amount;
    threshold(abs(2* mDiff),mAbsDiff,Threshold,255,THRESH_BINARY_INV);

    mDiff.setTo(Scalar(0),mAbsDiff);
    //imshow("mDiff Multiplied",mDiff);

    add(mInput,mDiff,mOutput);

    return true;
}

//Credits: http://stackoverflow.com/questions/34527349/non-connecting-morphological-filter/34535023#34535023
bool SmoothContours(vector<vector<Point>> contours_Src,vector<vector<Point>> &contours_Smooth,int Radius,double Sigma )
{
    if(contours_Src.size()==0)
        return false;
    int FilterSize = 2 * Radius + 1;

    for (int id = 0; id < contours_Src.size(); id++)
    {
        size_t len = contours_Src[id].size() + 2 * Radius;
        size_t idx = (contours_Src[id].size() - Radius);
        vector<float> x, y;
        vector<float> xFilt, yFilt;
        vector<Point> contours_Smooth_Curr;

        for (size_t i = 0; i < len; i++)
        {
            x.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].x);
            y.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].y);
        }

        GaussianBlur(x, xFilt, Size(FilterSize, FilterSize), Sigma, Sigma);
        GaussianBlur(y, yFilt, Size(FilterSize, FilterSize), Sigma, Sigma);

        for (size_t i = Radius; i < contours_Src[id].size() + Radius; i++)
        {
            contours_Smooth_Curr.push_back(Point(xFilt[i], yFilt[i]));
        }
        contours_Smooth.push_back(contours_Smooth_Curr);
    }


    return true;
}

int main(int argc, char* argv[])
{
    string FileName_S="D:\\Image samples\\Input.png";

    Mat mSource,mThres,mResult,mSmoothEdge;
    mSource= imread(FileName_S,0);
    RNG rng(12345);

    if(mSource.empty())
    {
        cout<<"[Error] Invalid Input Image!";
        return 0;
    }
    mSmoothEdge= mSource.clone();
    mSmoothEdge.setTo(0);
    threshold(mSource,mThres,125,255,THRESH_BINARY_INV);
    imshow("Source mThres",mThres);

image description

    cvtColor(mSource,mResult,COLOR_GRAY2BGR);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    vector<vector<Point>> contours_Smoothed;
    int Radius=1;
    double Sigma=2.0;

    findContours( mThres.clone(), contours, hierarchy, RETR_TREE, CV_CHAIN_APPROX_SIMPLE );

    //Smoothing Contour Points using Gaussian Smooth!
    SmoothContours(contours,contours_Smoothed,Radius,Sigma);

    for( size_t i = 0; i< contours.size(); i++ )
    {
        /*Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( mResult, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );*/
        if(hierarchy[i][2]<0)//These are inner most child contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(0), -1, 8, vector<Vec4i>(), 0, Point() );
        }
        if(hierarchy[i][3]<0)//These are outer most parent contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(255), -1, 8, vector<Vec4i>(), 0, Point() );
        }
    }

    //imshow("Source Image",mResult);
    imshow("1) Smooth Edge Points",mSmoothEdge);

image description

    //Smoothing Edge using Modified Unsharp Masking
    mSmoothEdge.setTo(0);
    SmoothEdgeSingleChannel(mThres,mSmoothEdge,2.5,1.0,254);
    imshow("2) Smooth Edges",mSmoothEdge);

image description

    waitKey();
}

Here is a Sample implementation of Contour based and Image based edge-smoothing algorithms! Hope this helps!

#include <iostream>
#include "opencv2/opencv.hpp"

#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char* argv[])
{
    string FileName_S="D:\\Image samples\\Input.png";

    Mat mSource,mThres,mResult,mSmoothEdge;
    mSource= imread(FileName_S,0);
    RNG rng(12345);

    if(mSource.empty())
    {
        cout<<"[Error] Invalid Input Image!";
        return 0;
    }
    mSmoothEdge= mSource.clone();
    mSmoothEdge.setTo(0);
    threshold(mSource,mThres,125,255,THRESH_BINARY_INV);
    imshow("Source mThres",mThres);

image description

    cvtColor(mSource,mResult,COLOR_GRAY2BGR);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    vector<vector<Point>> contours_Smoothed;
    int Radius=1;
    double Sigma=2.0;

    findContours( mThres.clone(), contours, hierarchy, RETR_TREE, CV_CHAIN_APPROX_SIMPLE );

    //Smoothing Contour Points using Gaussian Smooth!
    SmoothContours(contours,contours_Smoothed,Radius,Sigma);

    for( size_t i = 0; i< contours.size(); i++ )
    {
        /*Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( mResult, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );*/
        if(hierarchy[i][2]<0)//These are inner most child contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(0), -1, 8, vector<Vec4i>(), 0, Point() );
        }
        if(hierarchy[i][3]<0)//These are outer most parent contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(255), -1, 8, vector<Vec4i>(), 0, Point() );
        }
    }

    //imshow("Source Image",mResult);
    imshow("1) Smooth Edge Points",mSmoothEdge);

image description

    //Smoothing Edge using Modified Unsharp Masking
    mSmoothEdge.setTo(0);
    SmoothEdgeSingleChannel(mThres,mSmoothEdge,2.5,1.0,254);
    imshow("2) Smooth Edges",mSmoothEdge);

image description

    waitKey();
}
bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) ;

bool SmoothEdge( Mat mInput_Bgr,Mat &mOutput_Bgr, double amount, double radius, uchar Threshold) 
{
    if(mInput_Bgr.empty())
    {
        return 0;
    }

    if(radius<1)
        radius=1;

    Mat mInput,mOutput;
    Mat mChannel[3];

    split(mInput_Bgr,mChannel);

    for (int i = 0; i < 3; i++)
    {
        mInput= mChannel[i];
        SmoothEdgeSingleChannel(mInput,mOutput,amount, radius,Threshold); 
        mOutput.copyTo(mChannel[i]);
    }
    merge(mChannel,3,mOutput_Bgr);


    return true;
}

bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) 
{
    if(mInput.empty())
    {
        return 0;
    }
    if(radius<1)
        radius=1;

    Mat mGSmooth,mDiff,mAbsDiff;
    mOutput = Mat(mInput.size(),mInput.type());

    GaussianBlur(mInput,mGSmooth,Size(0,0),radius); 
    //imshow("mGSmooth",mGSmooth);

    subtract(mGSmooth,mInput,mDiff);
    //imshow("mDiff",mDiff);

    mDiff*=amount;
    threshold(abs(2* mDiff),mAbsDiff,Threshold,255,THRESH_BINARY_INV);

    mDiff.setTo(Scalar(0),mAbsDiff);
    //imshow("mDiff Multiplied",mDiff);

    add(mInput,mDiff,mOutput);

    return true;
}

//Credits: http://stackoverflow.com/questions/34527349/non-connecting-morphological-filter/34535023#34535023
bool SmoothContours(vector<vector<Point>> contours_Src,vector<vector<Point>> &contours_Smooth,int Radius,double Sigma )
{
    if(contours_Src.size()==0)
        return false;
    int FilterSize = 2 * Radius + 1;

    for (int id = 0; id < contours_Src.size(); id++)
    {
        size_t len = contours_Src[id].size() + 2 * Radius;
        size_t idx = (contours_Src[id].size() - Radius);
        vector<float> x, y;
        vector<float> xFilt, yFilt;
        vector<Point> contours_Smooth_Curr;

        for (size_t i = 0; i < len; i++)
        {
            x.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].x);
            y.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].y);
        }

        GaussianBlur(x, xFilt, Size(FilterSize, FilterSize), Sigma, Sigma);
        GaussianBlur(y, yFilt, Size(FilterSize, FilterSize), Sigma, Sigma);

        for (size_t i = Radius; i < contours_Src[id].size() + Radius; i++)
        {
            contours_Smooth_Curr.push_back(Point(xFilt[i], yFilt[i]));
        }
        contours_Smooth.push_back(contours_Smooth_Curr);
    }


    return true;
}

int main(int argc, char* argv[])
{
    string FileName_S="D:\\Image samples\\Input.png";

    Mat mSource,mThres,mResult,mSmoothEdge;
    mSource= imread(FileName_S,0);
    RNG rng(12345);

    if(mSource.empty())
    {
        cout<<"[Error] Invalid Input Image!";
        return 0;
    }
    mSmoothEdge= mSource.clone();
    mSmoothEdge.setTo(0);
    threshold(mSource,mThres,125,255,THRESH_BINARY_INV);
    imshow("Source mThres",mThres);

image description

    cvtColor(mSource,mResult,COLOR_GRAY2BGR);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    vector<vector<Point>> contours_Smoothed;
    int Radius=1;
    double Sigma=2.0;

    findContours( mThres.clone(), contours, hierarchy, RETR_TREE, CV_CHAIN_APPROX_SIMPLE );

    //Smoothing Contour Points using Gaussian Smooth!
    SmoothContours(contours,contours_Smoothed,Radius,Sigma);

    for( size_t i = 0; i< contours.size(); i++ )
    {
        /*Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( mResult, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );*/
        if(hierarchy[i][2]<0)//These are inner most child contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(0), -1, 8, vector<Vec4i>(), 0, Point() );
        }
        if(hierarchy[i][3]<0)//These are outer most parent contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(255), -1, 8, vector<Vec4i>(), 0, Point() );
        }
    }

    //imshow("Source Image",mResult);
    imshow("1) Smooth Edge Points",mSmoothEdge);

image description

    //Smoothing Edge using Modified Unsharp Masking
    mSmoothEdge.setTo(0);
    SmoothEdgeSingleChannel(mThres,mSmoothEdge,2.5,1.0,254);
    imshow("2) Smooth Edges",mSmoothEdge);

image description

    waitKey();
}

Here is a Sample implementation of Contour based and Image based edge-smoothing algorithms! Hope this helps!

#include <iostream>
#include "opencv2/opencv.hpp"

#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char* argv[])
{
    string FileName_S="D:\\Image samples\\Input.png";

    Mat mSource,mThres,mResult,mSmoothEdge;
    mSource= imread(FileName_S,0);
    RNG rng(12345);

    if(mSource.empty())
    {
        cout<<"[Error] Invalid Input Image!";
        return 0;
    }
    mSmoothEdge= mSource.clone();
    mSmoothEdge.setTo(0);
    threshold(mSource,mThres,125,255,THRESH_BINARY_INV);
    imshow("Source mThres",mThres);

image description

    cvtColor(mSource,mResult,COLOR_GRAY2BGR);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    vector<vector<Point>> contours_Smoothed;
    int Radius=1;
    double Sigma=2.0;

    findContours( mThres.clone(), contours, hierarchy, RETR_TREE, CV_CHAIN_APPROX_SIMPLE );

    //Smoothing Contour Points using Gaussian Smooth!
    SmoothContours(contours,contours_Smoothed,Radius,Sigma);

    for( size_t i = 0; i< contours.size(); i++ )
    {
        /*Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( mResult, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );*/
        if(hierarchy[i][2]<0)//These are inner most child contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(0), -1, 8, vector<Vec4i>(), 0, Point() );
        }
        if(hierarchy[i][3]<0)//These are outer most parent contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(255), -1, 8, vector<Vec4i>(), 0, Point() );
        }
    }

    //imshow("Source Image",mResult);
    imshow("1) Smooth Edge Points",mSmoothEdge);

image description

    //Smoothing Edge using Modified Unsharp Masking
    mSmoothEdge.setTo(0);
    SmoothEdgeSingleChannel(mThres,mSmoothEdge,2.5,1.0,254);
    imshow("2) Smooth Edges",mSmoothEdge);

image description

    waitKey();
}

bool SmoothEdge( Mat mInput_Bgr,Mat &mOutput_Bgr, double amount, double radius, uchar Threshold) 
{
    if(mInput_Bgr.empty())
    {
        return 0;
    }

    if(radius<1)
        radius=1;

    Mat mInput,mOutput;
    Mat mChannel[3];

    split(mInput_Bgr,mChannel);

    for (int i = 0; i < 3; i++)
    {
        mInput= mChannel[i];
        SmoothEdgeSingleChannel(mInput,mOutput,amount, radius,Threshold); 
        mOutput.copyTo(mChannel[i]);
    }
    merge(mChannel,3,mOutput_Bgr);


    return true;
}

bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) ;

bool SmoothEdge( Mat mInput_Bgr,Mat &mOutput_Bgr, double amount, double radius, uchar Threshold) 
{
    if(mInput_Bgr.empty())
    {
        return 0;
    }

    if(radius<1)
        radius=1;

    Mat mInput,mOutput;
    Mat mChannel[3];

    split(mInput_Bgr,mChannel);

    for (int i = 0; i < 3; i++)
    {
        mInput= mChannel[i];
        SmoothEdgeSingleChannel(mInput,mOutput,amount, radius,Threshold); 
        mOutput.copyTo(mChannel[i]);
    }
    merge(mChannel,3,mOutput_Bgr);


    return true;
}

bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) 
{
    if(mInput.empty())
    {
        return 0;
    }
    if(radius<1)
        radius=1;

    Mat mGSmooth,mDiff,mAbsDiff;
    mOutput = Mat(mInput.size(),mInput.type());

    GaussianBlur(mInput,mGSmooth,Size(0,0),radius); 
    //imshow("mGSmooth",mGSmooth);

    subtract(mGSmooth,mInput,mDiff);
    //imshow("mDiff",mDiff);

    mDiff*=amount;
    threshold(abs(2* mDiff),mAbsDiff,Threshold,255,THRESH_BINARY_INV);

    mDiff.setTo(Scalar(0),mAbsDiff);
    //imshow("mDiff Multiplied",mDiff);

    add(mInput,mDiff,mOutput);

    return true;
}

//Credits: http://stackoverflow.com/questions/34527349/non-connecting-morphological-filter/34535023#34535023
bool SmoothContours(vector<vector<Point>> contours_Src,vector<vector<Point>> &contours_Smooth,int Radius,double Sigma )
{
    if(contours_Src.size()==0)
        return false;
    int FilterSize = 2 * Radius + 1;

    for (int id = 0; id < contours_Src.size(); id++)
    {
        size_t len = contours_Src[id].size() + 2 * Radius;
        size_t idx = (contours_Src[id].size() - Radius);
        vector<float> x, y;
        vector<float> xFilt, yFilt;
        vector<Point> contours_Smooth_Curr;

        for (size_t i = 0; i < len; i++)
        {
            x.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].x);
            y.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].y);
        }

        GaussianBlur(x, xFilt, Size(FilterSize, FilterSize), Sigma, Sigma);
        GaussianBlur(y, yFilt, Size(FilterSize, FilterSize), Sigma, Sigma);

        for (size_t i = Radius; i < contours_Src[id].size() + Radius; i++)
        {
            contours_Smooth_Curr.push_back(Point(xFilt[i], yFilt[i]));
        }
        contours_Smooth.push_back(contours_Smooth_Curr);
    }


    return true;
}

Here is a Sample implementation of Contour based and Image based edge-smoothing algorithms! Hope this helps!

#include <iostream>
#include "opencv2/opencv.hpp"

#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char* argv[])
{
    string FileName_S="D:\\Image samples\\Input.png";

    Mat mSource,mThres,mResult,mSmoothEdge;
    mSource= imread(FileName_S,0);
    RNG rng(12345);

    if(mSource.empty())
    {
        cout<<"[Error] Invalid Input Image!";
        return 0;
    }
    mSmoothEdge= mSource.clone();
    mSmoothEdge.setTo(0);
    threshold(mSource,mThres,125,255,THRESH_BINARY_INV);
threshold(mSource,mThres,125,255,THRESH_BINARY);
    imshow("Source mThres",mThres);

image description

    cvtColor(mSource,mResult,COLOR_GRAY2BGR);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    vector<vector<Point>> contours_Smoothed;
    int Radius=1;
    double Sigma=2.0;

    findContours( mThres.clone(), contours, hierarchy, RETR_TREE, CV_CHAIN_APPROX_SIMPLE );

    //Smoothing Contour Points using Gaussian Smooth!
    SmoothContours(contours,contours_Smoothed,Radius,Sigma);

    for( size_t i = 0; i< contours.size(); i++ )
    {
        /*Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( mResult, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );*/
        if(hierarchy[i][2]<0)//These are inner most child contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(0), -1, 8, LINE_AA, vector<Vec4i>(), 0, Point() );
        }
        if(hierarchy[i][3]<0)//These are outer most parent contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(255), -1, 8, LINE_AA, vector<Vec4i>(), 0, Point() );
        }
    }

    //imshow("Source Image",mResult);
    imshow("1) Smooth Edge Points",mSmoothEdge);

image descriptionimage description

    //Smoothing Edge using Modified Unsharp Masking
    mSmoothEdge.setTo(0);
    SmoothEdgeSingleChannel(mThres,mSmoothEdge,2.5,1.0,254);
    imshow("2) Smooth Edges",mSmoothEdge);

image descriptionimage description

    waitKey();
}

bool SmoothEdge( Mat mInput_Bgr,Mat &mOutput_Bgr, double amount, double radius, uchar Threshold) 
{
    if(mInput_Bgr.empty())
    {
        return 0;
    }

    if(radius<1)
        radius=1;

    Mat mInput,mOutput;
    Mat mChannel[3];

    split(mInput_Bgr,mChannel);

    for (int i = 0; i < 3; i++)
    {
        mInput= mChannel[i];
        SmoothEdgeSingleChannel(mInput,mOutput,amount, radius,Threshold); 
        mOutput.copyTo(mChannel[i]);
    }
    merge(mChannel,3,mOutput_Bgr);


    return true;
}

bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) 
{
    if(mInput.empty())
    {
        return 0;
    }
    if(radius<1)
        radius=1;

    Mat mGSmooth,mDiff,mAbsDiff;
    mOutput = Mat(mInput.size(),mInput.type());

    GaussianBlur(mInput,mGSmooth,Size(0,0),radius); 
    //imshow("mGSmooth",mGSmooth);

    subtract(mGSmooth,mInput,mDiff);
    //imshow("mDiff",mDiff);

    mDiff*=amount;
    threshold(abs(2* mDiff),mAbsDiff,Threshold,255,THRESH_BINARY_INV);

    mDiff.setTo(Scalar(0),mAbsDiff);
    //imshow("mDiff Multiplied",mDiff);

    add(mInput,mDiff,mOutput);

    return true;
}

//Credits: http://stackoverflow.com/questions/34527349/non-connecting-morphological-filter/34535023#34535023
bool SmoothContours(vector<vector<Point>> contours_Src,vector<vector<Point>> &contours_Smooth,int Radius,double Sigma )
{
    if(contours_Src.size()==0)
        return false;
    int FilterSize = 2 * Radius + 1;

    for (int id = 0; id < contours_Src.size(); id++)
    {
        size_t len = contours_Src[id].size() + 2 * Radius;
        size_t idx = (contours_Src[id].size() - Radius);
        vector<float> x, y;
        vector<float> xFilt, yFilt;
        vector<Point> contours_Smooth_Curr;

        for (size_t i = 0; i < len; i++)
        {
            x.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].x);
            y.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].y);
        }

        GaussianBlur(x, xFilt, Size(FilterSize, FilterSize), Sigma, Sigma);
        GaussianBlur(y, yFilt, Size(FilterSize, FilterSize), Sigma, Sigma);

        for (size_t i = Radius; i < contours_Src[id].size() + Radius; i++)
        {
            contours_Smooth_Curr.push_back(Point(xFilt[i], yFilt[i]));
        }
        contours_Smooth.push_back(contours_Smooth_Curr);
    }


    return true;
}

Here is a Sample implementation of Contour based and Image based edge-smoothing algorithms! Hope this helps!

#include <iostream>
#include "opencv2/opencv.hpp"

#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char* argv[])
{
    string FileName_S="D:\\Image samples\\Input.png";

    Mat mSource,mThres,mResult,mSmoothEdge;
    mSource= imread(FileName_S,0);

//Source Image

image description

    RNG rng(12345);

    if(mSource.empty())
    {
        cout<<"[Error] Invalid Input Image!";
        return 0;
    }
    mSmoothEdge= mSource.clone();
    mSmoothEdge.setTo(0);
    threshold(mSource,mThres,125,255,THRESH_BINARY);
    imshow("Source mThres",mThres);

image description

    cvtColor(mSource,mResult,COLOR_GRAY2BGR);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    vector<vector<Point>> contours_Smoothed;
    int Radius=1;
    double Sigma=2.0;

    findContours( mThres.clone(), contours, hierarchy, RETR_TREE, CV_CHAIN_APPROX_SIMPLE );

    //Smoothing Contour Points using Gaussian Smooth!
    SmoothContours(contours,contours_Smoothed,Radius,Sigma);

    for( size_t i = 0; i< contours.size(); i++ )
    {
        /*Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( mResult, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );*/
        if(hierarchy[i][2]<0)//These are inner most child contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(0), -1, LINE_AA, vector<Vec4i>(), 0, Point() );
        }
        if(hierarchy[i][3]<0)//These are outer most parent contours
        {
            drawContours( mSmoothEdge, contours_Smoothed, i, Scalar(255), -1, LINE_AA, vector<Vec4i>(), 0, Point() );
        }
    }

    //imshow("Source Image",mResult);
    imshow("1) Smooth Edge Points",mSmoothEdge);

image description

    //Smoothing Edge using Modified Unsharp Masking
    mSmoothEdge.setTo(0);
    SmoothEdgeSingleChannel(mThres,mSmoothEdge,2.5,1.0,254);
    imshow("2) Smooth Edges",mSmoothEdge);

image description

    waitKey();
}

bool SmoothEdge( Mat mInput_Bgr,Mat &mOutput_Bgr, double amount, double radius, uchar Threshold) 
{
    if(mInput_Bgr.empty())
    {
        return 0;
    }

    if(radius<1)
        radius=1;

    Mat mInput,mOutput;
    Mat mChannel[3];

    split(mInput_Bgr,mChannel);

    for (int i = 0; i < 3; i++)
    {
        mInput= mChannel[i];
        SmoothEdgeSingleChannel(mInput,mOutput,amount, radius,Threshold); 
        mOutput.copyTo(mChannel[i]);
    }
    merge(mChannel,3,mOutput_Bgr);


    return true;
}

bool SmoothEdgeSingleChannel( Mat mInput,Mat &mOutput, double amount, double radius, uchar Threshold) 
{
    if(mInput.empty())
    {
        return 0;
    }
    if(radius<1)
        radius=1;

    Mat mGSmooth,mDiff,mAbsDiff;
    mOutput = Mat(mInput.size(),mInput.type());

    GaussianBlur(mInput,mGSmooth,Size(0,0),radius); 
    //imshow("mGSmooth",mGSmooth);

    subtract(mGSmooth,mInput,mDiff);
    //imshow("mDiff",mDiff);

    mDiff*=amount;
    threshold(abs(2* mDiff),mAbsDiff,Threshold,255,THRESH_BINARY_INV);

    mDiff.setTo(Scalar(0),mAbsDiff);
    //imshow("mDiff Multiplied",mDiff);

    add(mInput,mDiff,mOutput);

    return true;
}

//Credits: http://stackoverflow.com/questions/34527349/non-connecting-morphological-filter/34535023#34535023
bool SmoothContours(vector<vector<Point>> contours_Src,vector<vector<Point>> &contours_Smooth,int Radius,double Sigma )
{
    if(contours_Src.size()==0)
        return false;
    int FilterSize = 2 * Radius + 1;

    for (int id = 0; id < contours_Src.size(); id++)
    {
        size_t len = contours_Src[id].size() + 2 * Radius;
        size_t idx = (contours_Src[id].size() - Radius);
        vector<float> x, y;
        vector<float> xFilt, yFilt;
        vector<Point> contours_Smooth_Curr;

        for (size_t i = 0; i < len; i++)
        {
            x.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].x);
            y.push_back(contours_Src[id][(idx + i) % contours_Src[id].size()].y);
        }

        GaussianBlur(x, xFilt, Size(FilterSize, FilterSize), Sigma, Sigma);
        GaussianBlur(y, yFilt, Size(FilterSize, FilterSize), Sigma, Sigma);

        for (size_t i = Radius; i < contours_Src[id].size() + Radius; i++)
        {
            contours_Smooth_Curr.push_back(Point(xFilt[i], yFilt[i]));
        }
        contours_Smooth.push_back(contours_Smooth_Curr);
    }


    return true;
}