Store details of a binary image consisting simple polygons

后端 未结 4 1488
礼貌的吻别
礼貌的吻别 2020-12-22 14:56

This question relates to somewhat practice and experienced based process. I have an Mat binary image which consist of simple white color polygons in a black background. Actu

相关标签:
4条回答
  • 2020-12-22 15:24

    You can simply use findContours, with an appropriate contour approximation method. Basically, aside from CV_CHAIN_APPROX_NONE that will store all points, every other method is fine for this example: CV_CHAIN_APPROX_SIMPLE, CV_CHAIN_APPROX_TC89_L1 and CV_CHAIN_APPROX_TC89_KCOS.

    You can store those points in your database. You can then reload those points, and draw original image with fillPoly.

    This simple example show the retrieved contours points with the approximation method, and how to re-draw the image with those points.

    Note that you're image is aliased (you probably saved it in jpeg before png), so you need to remove aliasing for example keeping only points with value equals to 255.

    #include <opencv2\opencv.hpp>
    #include <vector>
    using namespace std;
    using namespace cv;
    
    int main()
    {
        Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
    
        // Removing compression artifacts
        img = img == 255;
    
        vector<vector<Point>> contours;
        findContours(img.clone(), contours, RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    
        if (contours.empty()) {return -1;}
    
        // Save the vertices "contours[0]"
    
        // Result image
        Mat3b res;
        cvtColor(img, res, COLOR_GRAY2BGR);
        for (int i = 0; i < contours[0].size(); ++i)
        {
            circle(res, contours[0][i], 3, Scalar(0,255,0));
        }
    
        // Reconstruct image from contours vertex
    
        // Load the vertices
        vector<vector<Point>> vertices = { contours[0] };
    
        Mat1b rec(img.rows, img.cols, uchar(0));
        fillPoly(rec, vertices, Scalar(255));
    
        imshow("Vertices", res);
        imshow("Reconstructed", rec);
        waitKey();
    
        return 0;
    }
    

    Green vertices with contour approximation method:

    0 讨论(0)
  • 2020-12-22 15:30

    It depends how generic the polygons can be. If the edges of the polygon are always parallel to x and y axes, then you could look at pixels in 8-neigborhood of a particular pixel and if there are odd number of white pixels you have found a corner. Or use a 4-neighborhood and test for even number of white pixels.

    0 讨论(0)
  • 2020-12-22 15:31

    A slightly off-the-wall approach... you could readily save the Mat as an image in OpenCV - preferably a PGM or a PNG since they are lossless. Then you could pass the image to a vector-tracer program like potrace and get it to tell you the outline in SVG format and store that in your database.

    So, potrace likes PGM files, so you either save your outline as a PGM in OpenCV or as a PNG, then you use ImageMagick to make that into a PGM and pass it to potrace like this:

    convert OpenCVImage.png pgm:- | potrace - -b svg -o file.svg
    

    which will get you an svg file like this:

    <?xml version="1.0" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
     "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
    <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
     width="3486.000000pt" height="4747.000000pt" viewBox="0 0 3486.000000 4747.000000"
     preserveAspectRatio="xMidYMid meet">
    <metadata>
    Created by potrace 1.13, written by Peter Selinger 2001-2015
    </metadata>
    <g transform="translate(0.000000,4747.000000) scale(0.100000,-0.100000)"
    fill="#000000" stroke="none">
    <path d="M0 23735 l0 -23735 17430 0 17430 0 0 23735 0 23735 -17430 0 -17430
    0 0 -23735z m20980 6560 l0 -3415 -399 0 c-293 0 -402 3 -407 12 -7 11 -68 11
    -2391 -9 l-781 -6 -6 -6576 c-3 -3617 -9 -6840 -12 -7163 l-6 -588 -1939 0
    -1939 0 0 10580 0 10580 3940 0 3940 0 0 -3415z"/>
    </g>
    </svg>
    

    You can view that in web-browser, by the way.

    You can recall the image at any time and re-create it with ImageMagick, or other tools, at the command line like this:

    convert outline.svg outline.png
    

    I would note that your entire PNG is actually only 32kB and storage is pretty cheap so it hardly seems worth the trouble to generate a vectorised image to save space. In fact, if you use a decent tool like ImageMagick and convert your image to a single bit PNG, it comes down to 6,150 bytes which is pretty small...

    convert YourBigInefficientOutline.png  NiceImageMagickOutlineOf6kB.png
    

    And, if you can handle reducing the outline in size to 1/5th of its original, which would still probably be adequate to locate the newspaper article, you could do:

    convert YourBig.png -resize 700x900 MySmall.png
    

    which weighs in at just 1,825 bytes.

    0 讨论(0)
  • 2020-12-22 15:33
    cv::Mat inputImage = cv::imread("input.png", CV_LOAD_IMAGE_GRAYSCALE);
    
    // find non-zero point coordinates
    cv::Mat nonZeroCoordinates;
    cv::findNonZero(inputImage, nonZeroCoordinates);
    

    Then you can save the nonZeroCoordinates matrix into your file to use.

    If you want to create a same image using these coordinates, you can do like this:

    std::vector<std::vector<cv::Point> > points;
    points.push_back(nonZeroCoordinates);
    
    cv::Mat output = cv::Mat::zeros(inputImage.size(), CV_8UC1);
    cv::fillPoly(output, points, cv::Scalar(255));
    

    Hope it helps!

    0 讨论(0)
提交回复
热议问题