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
Asked
Active
Viewed 3,724 times
1
-
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 Answers
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;
}
});

user9876747
- 21
- 4
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
-
-
@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