how to model complex shapes using representative points?

人盡茶涼 提交于 2019-12-12 14:35:14

问题


I want to reduce the amount of white pixels in this image to just some candidate or representative points in the output image (goal is to model different types of shapes)

if you just connect the gray points in the output image together you have the same path but with less white pixels. this path should have only one starting point and one ending point and covers all path from starting to ending.

I can solve it using CCA(connected component analysis) and some if then else rules ! but it seems slow.

I need this Algorithm to reduce the amount of pixels needed to describe shapes.

what is the fastest and accurate algorithm here ?

I also welcome those methods that can increase the accuracy of shape modeling by increasing the candidate points.


回答1:


  • skeletonize the shape to get a single-pixel-wide path (see https://en.wikipedia.org/wiki/Topological_skeleton)

  • represent the path as a chain of pixels.

  • select a number of pixels along the path, regularly spaced.

  • generate a Cardinal spline through these points (see https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline). A cubic spline is also possible.

  • for every section of the spline, estimate the deviation between the path in the image and the curve. This can be done by sampling a few points along the curve and finding the closest point on the section of the path (by trying all pixels).

  • when the deviation is too large, add one or more pixels in that section.

  • recompute the whole spline and repeat, until you don't need to insert points anymore.

By adjusting the deviation threshold, you can trade curve smoothness for matching accuracy.

It is possible to avoid recomputing the curve where no point insertion occurred, but this requires some care.




回答2:


I experimented with approximating the contour with polylines using approxPolyDP. When the shape is closed, this is simple and always produce a good approximation. But when the shape is open, like the sample image in question, sometimes the ordering of approximated points is not maintained (I've given an example of this below). It's difficult to describe every detail of the approach, so I've commented the c++ code as best as I can. Hope this helps.

Gradient direction at polyline approximated points (red dots are polyline approx. points offset by some amount in the opposite gradient direction, and blue dots are in gradient direction):

Final approximated points (a point within a red blob) and order of navigation (blue arrows)

Example shapes and gradient directions. Note the difference between closed and open shapes. For the closed shape, the red dots are all located to one side of the stroke. For open shapes, red dots are located on both sides of the stroke.

Shape on the top left gets the order of navigation wrong because the contour doesn't start from a tip of the shape. However, it does a good pointwise approximation.

int _tmain(int argc, _TCHAR* argv[])
{
    Mat im = imread("1.png", 0);
    Mat roi = Mat::zeros(im.size(), CV_8UC1);
    /* find the gradient at every point */
    Mat dx, dy;
    Sobel(im, dx, CV_32F, 1, 0, 7);
    Sobel(im, dy, CV_32F, 0, 1, 7);
    /* take the distance transform */
    Mat dist;
    distanceTransform(im, dist, CV_DIST_L2, 5);
    /* max stroke radius */
    double th;
    minMaxLoc(dist, NULL, &th);

    /* for display/debug purposes */
    Mat rgb = Mat::zeros(im.size(), CV_8UC3);
    Mat rgb2;
    cvtColor(im, rgb2, CV_GRAY2BGR);
    Mat rgb3 = Mat::zeros(im.size(), CV_8UC3);

    Mat tmp = im.clone();
    // find contours, get every point with CV_CHAIN_APPROX_NONE
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(tmp, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, Point(0, 0));

    for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
    {
        /* draw contours */
        drawContours(rgb, contours, idx, Scalar(0, 255, 255), 2, 8);
        drawContours(rgb2, contours, idx, Scalar(0, 255, 255), 1, 8);
        drawContours(rgb3, contours, idx, Scalar(0, 255, 255), 1, 8);

        /* polyline approximztion of the contour */
        vector<Point> poly;
        approxPolyDP(contours[idx], poly, th, false);
        /* 
            now we'll sample the gradient along the points in the polyline, 
            find another opposite point in the coitour in the gradient direction, 
            then find the peak location in the distance image (looks like the
            mid point should also work, but I didn't try it).
        */
        for (Point& pt: poly)
        {
            /* sample dx, dy at our point of interest */
            float x = dx.at<float>(pt);
            float y = dy.at<float>(pt);
            float n = sqrtf(x*x + y*y);
            /*  
                select another point in the direction of the gradient that intersects the stroke:
                by choosing a point that's around 2.5 times the max stroke radius, we hope
                to cross the stroke with this line 
            */
            Point pt2(pt.x + 2.5*th*x/n, pt.y + 2.5*th*y/n);
            /* offset the first point a bit in the opposite gradient direction */
            Point pt1(pt.x - .5*th*x/n, pt.y - .5*th*y/n);
            /* draw a thick line */
            line(roi, pt1, pt2, Scalar(255, 255, 255), 2, 8);
            /*
                display the points
            */
            line(rgb3, pt1, pt2, Scalar(255, 255, 255), 2, 8);
            line(rgb2, pt1, pt2, Scalar(0, 255, 255), 2, CV_AA);
            circle(rgb2, pt1, 3, Scalar(0, 0, 255), -1, CV_AA);
            circle(rgb2, pt2, 3, Scalar(255, 0, 0), -1, CV_AA);
        }   

        /* dilate the lines so that nearby lines can merge */
        Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
        morphologyEx(roi, roi, CV_MOP_DILATE, kernel, Point(-1, -1), 1);
        /* only for debug */
        morphologyEx(rgb3, rgb3, CV_MOP_DILATE, kernel, Point(-1, -1), 1);
        /* we are interested in lines segments that are within the shape */
        roi &= im;
        /* collect all these line segments */
        vector<vector<Point>> regions;
        findContours(roi, regions, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
        /* 
            now that we have all the info about lines segments, 
            we can use the image for other purposes such as a mask
        */
        roi.setTo(Scalar(0, 0, 0));
        /* our points of interest when we approximate the shape */
        vector<Point> shapeApprox;
        /*
            for each point on the shape contour, see if it is within a line segment
            that we found using gradients. if so, find the peak location from the distance image.
            it is a point in the skeleton
        */
        for (Point& pt: contours[idx])
        {
            for (size_t i = 0; i < regions.size(); i++)
            {
                if (-1 != pointPolygonTest(regions[i], pt, false))
                {
                    /* using roi as a mask to find the peak location from distance image */
                    drawContours(roi, regions, i, Scalar(255, 255, 255), -1);
                    double mx;
                    Point mxLoc;
                    minMaxLoc(dist, NULL, &mx, NULL, &mxLoc, roi);
                    /* 
                        if this point is not already in the list, add it. 
                        as the gradient line can intersect the shape contour at two
                        points most of the time, we'll find the same peak twice
                    */
                    if (shapeApprox.end() == find(shapeApprox.begin(), shapeApprox.end(), mxLoc))
                    {
                        //cout << mx << " @ " << mxLoc << endl;
                        shapeApprox.push_back(mxLoc);
                    }
                    /* no need to test other gradient lines */
                    break;
                }
            }
            /* reset the mask */
            roi.setTo(Scalar(0, 0, 0));
        }
        /* draw the (possibly merged) gradient line segments */
        drawContours(rgb, regions, -1, Scalar(0, 0, 255), -1);
        /* draw the shape approximation */
        for (size_t i = 1; i < shapeApprox.size(); i++)
        {
            arrowedLine(rgb, shapeApprox[i-1], shapeApprox[i], Scalar(255, 0, 0), 2, CV_AA, 0, .1);
            //imshow("approx", rgb);
            //waitKey(0);
        }
    }

    imshow("debug", rgb3);
    imshow("points", rgb2);
    imshow("approx", rgb);
    waitKey(0);

    return 0;
}



回答3:


Have you checked skeletonization techniques? There are some examples available... Check https://sites.google.com/site/rameyarnaud/research/c/voronoi for example



来源:https://stackoverflow.com/questions/44199153/how-to-model-complex-shapes-using-representative-points

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