-1

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. Actually those polygons represent an article in a newspaper page. So what I want is to store the details of the location of the article inside the newspaper page. One Mat image has only one polygon in it. So one option is to

  1. Use pure OpenCV calls to store Mat into a .xml or .yml file (How to write a Float Mat to a file in OpenCV : accepted answer)
  2. Find the coordinates of the polygon where there are vertices and store only those coordinates into the database

Following is a sample image of a Mat I am going to store.

enter image description here

The first option seems possible but I don't know how to implement the second approach. If it is possible, that would be most efficient as I think, because then there would be only few coordinates to be saved for each article. I can implement a complex procedure to find the vertices for that and also to redraw the Mat image using those coordinates when needed. But I hope there is a simple process in opencv for this task.

So what I want to know is which approach is better and if the second approach is better, how to do that in opencv with c++. I am neither an opencv expert nor c++ expert so an appropriate answer would save me many hours and also efficiency of the program.

Community
  • 1
  • 1
Samitha Chathuranga
  • 1,689
  • 5
  • 30
  • 57
  • Use _findContours_ with _method_ parameter as CV_CHAIN_APPROX_SIMPLE. In the documentation is says **_CV_CHAIN_APPROX_SIMPLE compresses horizontal, vertical, and diagonal segments and leaves only their end points. For example, an up-right rectangular contour is encoded with 4 points._** – dhanushka Nov 11 '15 at 10:38
  • What will be the output of findContours method in here? I am curious with this example > http://docs.opencv.org/2.4/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html – Samitha Chathuranga Nov 11 '15 at 10:59
  • _findContours_ with CV_CHAIN_APPROX_SIMPLE will most likely output something similar to what you expect in your second approach. – dhanushka Nov 11 '15 at 11:38
  • @dhanushka So it should give a set of coordinates of points. But I'm doubtful which parameter of the method outputs those coordinates. Is it OutputArrayOfArrays contours parameter?? – Samitha Chathuranga Nov 11 '15 at 11:49
  • Yes. In the example link you mentioned earlier, it is `vector > contours;`. – dhanushka Nov 11 '15 at 12:04
  • Ok, Thanx. I will reply back with the results. – Samitha Chathuranga Nov 11 '15 at 12:10
  • @E_learner The previous question is not the same as this. It has a wider scope and questions on the possibility of using object writing approach too and asks about the best practical approach. But this is specific and I thought that this part should go in a separate question, as questions requires to be more specific and the problem should be stated clearly. Respect your eagerness on preserving stackoverflow standards though !!! – Samitha Chathuranga Nov 11 '15 at 17:57

4 Answers4

1

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.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • 1
    By the way, the built-in Harris Corner Detector within OpenCV does a great job of finding the corners... https://github.com/Itseez/opencv/blob/master/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp And you could then recreate the outline using the *"Gift Wrapping Algorithm"* here... https://en.wikipedia.org/wiki/Gift_wrapping_algorithm – Mark Setchell Nov 11 '15 at 17:02
  • I will try it. Thanx. By the way the process in your answer is interesting. But seems that it is making the things more complex as storing original .png images seems cheap. Specially using other tools is making things more complex – Samitha Chathuranga Nov 12 '15 at 03:12
1
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!

  • As I see, this is not very different from keeping the original Mat object. Here u are creating a new Mat object nonZeroCoordinates to represent the original one (this might be consuming less space) and then using it back to reproduce the original Mat. I want to store the details in a file. – Samitha Chathuranga Nov 11 '15 at 11:03
  • What kind of details are you looking for? –  Nov 11 '15 at 11:28
  • The coordinates of the vertices of the polygon. That is what I want to store in a database or a file and then use back to regenerate the original image. – Samitha Chathuranga Nov 11 '15 at 11:56
  • Have you tried finding contours and then using convexHull? Take a reference here: http://stackoverflow.com/questions/33625186/crop-triangle-with-opencv-c/33627280#33627280 –  Nov 11 '15 at 12:23
1

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.

SimonFojtu
  • 564
  • 5
  • 13
1

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:

enter image description here

Miki
  • 40,887
  • 13
  • 123
  • 202
  • Hi Miki.. :-) Thanks a lot for the answer. I will reply back with results. By the way is there any possibility of missing any vertices or moving the their coordinates slightly away while finding them or regenerating the image back? – Samitha Chathuranga Nov 14 '15 at 16:02
  • @SamithaChathuranga no, vertices and _reconstructed_ matrix will be consistent. Remember to save you original images in png (or any other lossless format) to avoid compression artifacts, whose removal will modify the original shape and will give you inaccurate vertices. – Miki Nov 15 '15 at 17:11
  • Ok, Yes I am saving images in .png format (it was converted to jpg only to upload to the question) – Samitha Chathuranga Nov 16 '15 at 13:15