OpenCV sharpen the edges (edges with no holes)

筅森魡賤 提交于 2019-11-29 05:17:26
Nejc

I managed to obtain a pretty nice image of the edge by computing an approximation of the absolute value of gradient of the input image.

EDIT: Before I started working, I resized the input image to 5x smaller size. Click here to see it!. If you use my code on that image, the results will be good. If you want to make my code work well with the image of the original size, then either:

  • multiply Gaussian kernel sizes and sigmas by 5, or
  • downsample the image by factor 5, execute the algorithm and then upsample the result by factor 5 (this should work much faster than the first option)

This is the result I got:

My procedure relies on two key features. The first is a conversion to appropriate color space. As Jeru Luke stated in his answer , the saturation channel in HSV color space is the good choice here. The second important thing is the estimation of absolute value of gradient. I used sobel operators and some arithmetics for that purpose. I can provide additional explanations if someone requests them.

This is the code I used to obtain the first image.

using namespace std;
using namespace cv;

Mat img_rgb = imread("letter.jpg");

Mat img_hsv;
cvtColor(img_rgb, img_hsv, CV_BGR2HSV);
vector<Mat> channels_hsv;
split(img_hsv, channels_hsv);

Mat channel_s = channels_hsv[1];
GaussianBlur(channel_s, channel_s, Size(9, 9), 2, 2);

Mat imf;
channel_s.convertTo(imf, CV_32FC1, 0.5f, 0.5f);

Mat sobx, soby;
Sobel(imf, sobx, -1, 1, 0);
Sobel(imf, soby, -1, 0, 1);

sobx = sobx.mul(sobx);
soby = soby.mul(soby);

Mat grad_abs_val_approx;
cv::pow(sobx + soby, 0.5, grad_abs_val_approx);

Mat filtered;
GaussianBlur(grad_abs_val_approx, filtered, Size(9, 9), 2, 2);

Scalar mean, stdev;
meanStdDev(filtered, mean, stdev);

Mat thresholded;
cv::threshold(filtered, thresholded, mean.val[0] + stdev.val[0], 1.0, CV_THRESH_TOZERO);

// I scale the image at this point so that it is displayed properly 
imshow("image", thresholded/50);

And this is how I computed the second image:

Mat thresholded_bin;
cv::threshold(filtered, thresholded_bin, mean.val[0] + stdev.val[0], 1.0, CV_THRESH_BINARY);

Mat converted;
thresholded_bin.convertTo(converted, CV_8UC1);

vector<vector<Point>> contours;
findContours(converted, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);

Mat contour_img = Mat::zeros(converted.size(), CV_8UC1);
drawContours(contour_img, contours, -1, 255);

imshow("contours", contour_img);

Thanks for yours comments and suggestion. The code provided by @NEJC works perfectly and cover 80% of my use case.

Nevertheless, it does not works with similar case like this case not solved by the current code and i don't know why.

Perhaps someone have an idea/clue/solution ?

I continue to improve the code and try to find a more generic solution that can cover more case. I will post it if i ever i find.

In any case, below is the working code based on @NEJC solution and notes.

public static Mat process(Mat original){
    Mat src = original.clone();
    Mat hsvMat = new Mat();
    Mat saturation = new Mat();
    Mat sobx = new Mat();
    Mat soby = new Mat();
    Mat grad_abs_val_approx = new Mat();

    Imgproc.cvtColor(src, hsvMat, Imgproc.COLOR_BGR2HSV);
    List<Mat> hsv_channels = new ArrayList<Mat>(3);
    Core.split(hsvMat, hsv_channels);
    Mat hue = hsv_channels.get( 0 );
    Mat sat = hsv_channels.get( 1 );
    Mat val = hsv_channels.get( 2 );

    Imgproc.GaussianBlur(sat, saturation, new Size(9, 9), 2, 2);
    Mat imf = new Mat();
    saturation.convertTo(imf, CV_32FC1, 0.5f, 0.5f);

    Imgproc.Sobel(imf, sobx, -1, 1, 0);
    Imgproc.Sobel(imf, soby, -1, 0, 1);

    sobx = sobx.mul(sobx);
    soby = soby.mul(soby);

    Mat sumxy = new Mat();
    Core.add(sobx,soby, sumxy);
    Core.pow(sumxy, 0.5, grad_abs_val_approx);

    sobx.release();
    soby.release();
    sumxy.release();;


    Mat filtered = new Mat();
    Imgproc.GaussianBlur(grad_abs_val_approx, filtered, new Size(9, 9), 2, 2);

    final MatOfDouble mean = new MatOfDouble();
    final MatOfDouble stdev = new MatOfDouble();
    Core.meanStdDev(filtered, mean, stdev);

    Mat thresholded = new Mat();
    Imgproc.threshold(filtered, thresholded, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_TOZERO);


    /*
    Mat thresholded_bin = new Mat();
    Imgproc.threshold(filtered, thresholded_bin, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_BINARY_INV);
    Mat converted = new Mat();
    thresholded_bin.convertTo(converted, CV_8UC1);
    */

    Mat converted = new Mat();
    thresholded.convertTo(converted, CV_8UC1);
    return converted;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!