1

I have found the contour but the problem is that the findcontours() returns contours in random order like at contour(0) it shows some sontour from middle of the page. How can I sort them vertically? From top to bottom and then left to right? Given the image below I connected the components horizontally and connected every MCQ with their choices and then applied findcontours() and Now I want to sort them in an order that they should be retrieved sequentially

enter image description here

Zain ul abdeen
  • 155
  • 1
  • 5
  • 15
  • Just an advice, if you trying to detect those little circle locations template matching on normalized images should give you good results. – ivan_a Apr 23 '15 at 03:51
  • @ivan_a couldn't understand , can you please elaborate? – Zain ul abdeen Apr 23 '15 at 06:28
  • Useful, but sometimes these algos can throw an exception: https://stackoverflow.com/questions/53538333/java-sorting-a-massive-arraylist-using-a-custom-comparator-sometimes-throws-iil – Riccardo Finazzi Nov 29 '18 at 11:50

3 Answers3

2

I have the same problem and use the sort method of java.

//sort by y coordinates using the topleft point of every contour's bounding box
    Collections.sort(contourList, new Comparator<MatOfPoint>() {
        @Override
        public int compare(MatOfPoint o1, MatOfPoint o2) {
            Rect rect1 = Imgproc.boundingRect(o1);
            Rect rect2 = Imgproc.boundingRect(o2);
            int result = Double.compare(rect1.tl().y, rect2.tl().y);
            return result;
        }
    } );


//sort by x coordinates
Collections.sort(contourList, new Comparator<MatOfPoint>() {
        @Override
        public int compare(MatOfPoint o1, MatOfPoint o2) {
            Rect rect1 = Imgproc.boundingRect(o1);
            Rect rect2 = Imgproc.boundingRect(o2);
            int result = 0;
            double total = rect1.tl().y/rect2.tl().y;
            if (total>=0.9 && total<=1.4 ){
                result = Double.compare(rect1.tl().x, rect2.tl().x);
            }
            return result;
        }
    });
1

You have the function boundingRect: With that you can find the topleft point of a bounding rectangle around the points. Then you can define a sorting function that orders the contours based on the location of this point for each contour.

Antonio
  • 19,451
  • 13
  • 99
  • 197
  • @HelpingDesk It will return a [Rect](http://docs.opencv.org/java/org/opencv/core/Rect.html), which has different methods, like `tl()` (top left point) and `br()` (bottom right point). – Antonio Apr 22 '15 at 21:28
  • I have used boundingrect() to crop the region but not familiar with how it actually does. Can you please help with some code? – Zain ul abdeen Apr 22 '15 at 21:31
  • @HelpingDesk I am sorry but I don't use the Java version. It's pretty tricky in fact. Maybe [this](http://stackoverflow.com/questions/29517069/how-are-two-element-vector-represented-in-a-opencv-mat-in-java) can give you a little help. – Antonio Apr 22 '15 at 21:38
  • I am bit confused with Tl().x and y can you pleaasse explain something? Thanks – Zain ul abdeen Apr 26 '15 at 22:57
  • @HelpingDesk Can you edit your question to explain better what is the results that you obtain and what is that you do not understand? As I said, I have no experience with the Java interface, so I have no ready made answers. – Antonio Apr 26 '15 at 23:38
  • What axis value does tl().x and tl().y gives? – Zain ul abdeen Apr 26 '15 at 23:44
  • @HelpingDesk Take a set of points. Build a containing bounding box. Take the top left corner. It has coordinates (x,y): y is the row number of the image (from top to bottom), x is the column number of the image (from left to right) – Antonio Apr 26 '15 at 23:50
  • Ok I understand please take a look at my other question. 1 and 16 are on the sane distance from top but their values have drastic difference. http://stackoverflow.com/questions/29884692/sort-bounding-boxes-with-tl-opencv-android – Zain ul abdeen Apr 26 '15 at 23:54
0

@user9876747 solution works great for small contour list but throws the below exception for larger contour list.

java.lang.IllegalArgumentException: Comparison method violates its general contract!

So i was able to tweak the solution to below and it works fine for large contour lists.

 Collections.sort(contours, new Comparator<MatOfPoint>() {
        public int compare(MatOfPoint o1, MatOfPoint o2) {
            org.opencv.core.Rect rect1 = Imgproc.boundingRect(o1);
            org.opencv.core.Rect rect2 = Imgproc.boundingRect(o2);


            int result = Double.compare(rect1.br().y, rect2.br().y);
            double total = rect1.br().y / rect2.br().y;

            if (total < 1.1 && total > 1/1.1) {
                result = Double.compare(rect1.tl().x, rect2.tl().x);
            }
            return result;
        }
    });
Ismail Osunlana
  • 434
  • 5
  • 9