OpenCV how to smooth contour, reducing noise
Asked Answered
J

2

6

I extracted the contours of an image, that you can see here: contour

However, it has some noise. How can I smooth the noise? I did a close up to make clearer what I want to meant enter image description here

Original image that I've used: enter image description here

Code:

rMaskgray = cv2.imread('redmask.jpg', cv2.CV_LOAD_IMAGE_GRAYSCALE)
(thresh, binRed) = cv2.threshold(rMaskgray, 50, 255, cv2.THRESH_BINARY)

Rcontours, hier_r = cv2.findContours(binRed,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
r_areas = [cv2.contourArea(c) for c in Rcontours]
max_rarea = np.max(r_areas)
CntExternalMask = np.ones(binRed.shape[:2], dtype="uint8") * 255

for c in Rcontours:
    if(( cv2.contourArea(c) > max_rarea * 0.70) and (cv2.contourArea(c)< max_rarea)):
        cv2.drawContours(CntExternalMask,[c],-1,0,1)

cv2.imwrite('contour1.jpg', CntExternalMask)
Jost answered 6/5, 2016 at 9:8 Comment(5)
Could you post the code you used to create this?Cornemuse
@MartinEvans Edited !Jost
What happens if your replace cv2.CHAIN_APPROX_SIMPLE by cv2.CHAIN_APPROX_NONE ?Ornithic
Please let us know the OpenCV version you are using, seems to be old code.Ornithic
I'm using 3.0 opencv. I'll update !Jost
O
5

Try an upgrade to OpenCV 3.1.0. After some code adaptations for the new version as shown below, I tried it out with OpenCV version 3.1.0 and did not see any of the effects you are describing.

import cv2
import numpy as np

print cv2.__version__

rMaskgray = cv2.imread('5evOn.jpg', 0)
(thresh, binRed) = cv2.threshold(rMaskgray, 50, 255, cv2.THRESH_BINARY)

_, Rcontours, hier_r = cv2.findContours(binRed,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
r_areas = [cv2.contourArea(c) for c in Rcontours]
max_rarea = np.max(r_areas)
CntExternalMask = np.ones(binRed.shape[:2], dtype="uint8") * 255

for c in Rcontours:
    if(( cv2.contourArea(c) > max_rarea * 0.70) and (cv2.contourArea(c)< max_rarea)):
        cv2.drawContours(CntExternalMask,[c],-1,0,1)

cv2.imwrite('contour1.jpg', CntExternalMask)

enter image description here

Ornithic answered 6/5, 2016 at 19:51 Comment(5)
I Still have some problems : dl.dropboxusercontent.com/u/710615/externalpcb.jpgJost
Did you ever find a solution for this? Sorry, an old question just having the same issue. @JostDeontology
@Jonathan: As mentioned above, the problems seem to be gone with the OpenCV version used above. If this is not the case for you, please post an example.Ornithic
@Ornithic Here's the issue I am having. I cant find a happy medium between jagged lines and rounded lines. #47936974Deontology
you may find a solution by digging here: morethantechnical.com/2012/12/07/…Collaborationist
I
1

I don't know if is it ok to provide Java code - but I implemented Gaussian smoothing for openCV contour. Logic and theory is taken from here https://www.morethantechnical.com/2012/12/07/resampling-smoothing-and-interest-points-of-curves-via-css-in-opencv-w-code/

package CurveTools;

import org.apache.log4j.Logger;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;

import java.util.ArrayList;
import java.util.List;

import static org.opencv.core.CvType.CV_64F;
import static org.opencv.imgproc.Imgproc.getGaussianKernel;

class CurveSmoother {

    private double[] g, dg, d2g, gx, dx, d2x;
    private double gx1, dgx1, d2gx1;

    public double[] kappa, smoothX, smoothY;
    public double[] contourX, contourY;

    /* 1st and 2nd derivative of 1D gaussian  */
    void getGaussianDerivs(double sigma, int M) {

        int L = (M - 1) / 2;
        double sigma_sq = sigma * sigma;
        double sigma_quad = sigma_sq * sigma_sq;

        dg = new double[M];
        d2g = new double[M];
        g = new double[M];

        Mat tmpG = getGaussianKernel(M, sigma, CV_64F);

        for (double i = -L; i < L + 1.0; i += 1.0) {
            int idx = (int) (i + L);

            g[idx] = tmpG.get(idx, 0)[0];

            // from http://www.cedar.buffalo.edu/~srihari/CSE555/Normal2.pdf
            dg[idx] = -i * g[idx] / sigma_sq;
            d2g[idx] = (-sigma_sq + i * i) * g[idx] / sigma_quad;
        }
    }

    /* 1st and 2nd derivative of smoothed curve point */
    void getdX(double[] x, int n, double sigma, boolean isOpen) {

        int L = (g.length - 1) / 2;

        gx1 = dgx1 = d2gx1 = 0.0;
        for (int k = -L; k < L + 1; k++) {
            double x_n_k;
            if (n - k < 0) {
                if (isOpen) {
                    //open curve - mirror values on border
                    x_n_k = x[-(n - k)];
                } else {
                    //closed curve - take values from end of curve
                    x_n_k = x[x.length + (n - k)];
                }
            } else if (n - k > x.length - 1) {
                if (isOpen) {
                    //mirror value on border
                    x_n_k = x[n + k];
                } else {
                    x_n_k = x[(n - k) - x.length];
                }
            } else {
                x_n_k = x[n - k];
            }

            gx1 += x_n_k * g[k + L]; //gaussians go [0 -> M-1]
            dgx1 += x_n_k * dg[k + L];
            d2gx1 += x_n_k * d2g[k + L];
        }
    }

    /* 0th, 1st and 2nd derivatives of whole smoothed curve */
    void getdXcurve(double[] x, double sigma, boolean isOpen) {

        gx = new double[x.length];
        dx = new double[x.length];
        d2x = new double[x.length];

        for (int i = 0; i < x.length; i++) {
            getdX(x, i, sigma, isOpen);
            gx[i] = gx1;
            dx[i] = dgx1;
            d2x[i] = d2gx1;
        }
    }

    /*
        compute curvature of curve after gaussian smoothing
        from "Shape similarity retrieval under affine transforms", Mokhtarian & Abbasi 2002
        curvex - x position of points
        curvey - y position of points
        kappa - curvature coeff for each point
        sigma - gaussian sigma
    */
    void computeCurveCSS(double[] curvex, double[] curvey, double sigma, boolean isOpen) {
        int M = (int) Math.round((10.0 * sigma + 1.0) / 2.0) * 2 - 1;
        assert (M % 2 == 1); //M is an odd number

        getGaussianDerivs(sigma, M);//, g, dg, d2g

        double[] X, XX, Y, YY;

        getdXcurve(curvex, sigma, isOpen);
        smoothX = gx.clone();
        X = dx.clone();
        XX = d2x.clone();

        getdXcurve(curvey, sigma, isOpen);
        smoothY = gx.clone();
        Y = dx.clone();
        YY = d2x.clone();


        kappa = new double[curvex.length];

        for (int i = 0; i < curvex.length; i++) {
            // Mokhtarian 02' eqn (4)
            kappa[i] = (X[i] * YY[i] - XX[i] * Y[i]) / Math.pow(X[i] * X[i] + Y[i] * Y[i], 1.5);
        }
    }

    /* find zero crossings on curvature */
    ArrayList<Integer> findCSSInterestPoints() {

        assert (kappa != null);

        ArrayList<Integer> crossings = new ArrayList<>();

        for (int i = 0; i < kappa.length - 1; i++) {
            if ((kappa[i] < 0.0 && kappa[i + 1] > 0.0) || kappa[i] > 0.0 && kappa[i + 1] < 0.0) {
                crossings.add(i);
            }
        }
        return crossings;
    }

    public void polyLineSplit(MatOfPoint pl) {
        contourX = new double[pl.height()];
        contourY = new double[pl.height()];

        for (int j = 0; j < contourX.length; j++) {
            contourX[j] = pl.get(j, 0)[0];
            contourY[j] = pl.get(j, 0)[1];
        }
    }

    public MatOfPoint polyLineMerge(double[] xContour, double[] yContour) {

        assert (xContour.length == yContour.length);

        MatOfPoint pl = new MatOfPoint();

        List<Point> list = new ArrayList<>();

        for (int j = 0; j < xContour.length; j++)
            list.add(new Point(xContour[j], yContour[j]));

        pl.fromList(list);

        return pl;
    }

    MatOfPoint smoothCurve(MatOfPoint curve, double sigma) {
        int M = (int) Math.round((10.0 * sigma + 1.0) / 2.0) * 2 - 1;
        assert (M % 2 == 1); //M is an odd number

        //create kernels
        getGaussianDerivs(sigma, M);
        polyLineSplit(curve);

        getdXcurve(contourX, sigma, false);
        smoothX = gx.clone();

        getdXcurve(contourY, sigma, false);
        smoothY = gx;

        Logger.getRootLogger().info("Smooth curve len: " + smoothX.length);

        return polyLineMerge(smoothX, smoothY);
    }
}
Immaculate answered 31/5, 2019 at 10:12 Comment(1)
What would be a good value for sigma?Scaly

© 2022 - 2024 — McMap. All rights reserved.