17
rect = cv2.minAreaRect(largest_contour)
rect = ((rect[0][0] * self.scale_down, rect[0][1] * self.scale_down), (rect[1][0] * self.scale_down, rect[1][1] * self.scale_down), rect[2])
box = cv2.cv.BoxPoints(rect)
print box 
box = np.int0(box)
cv2.drawContours(frame,[box], 0, (0, 0, 255), 2)

This is how my code looks like. I tried to print the box to see what's that and I got some printing result such as ((200.0, 472.0), (200.0, 228.0), (420.0, 228.0), (420.0, 472.0)). It should have something to do x and y coordinates right? I guess that's the four corners of the rectangle? So what are they exactly? Thanks!

pcs
  • 1,864
  • 4
  • 25
  • 49
Mandy
  • 483
  • 3
  • 7
  • 11

7 Answers7

38

The common misconception of the "box" values is that the first sub-list of the "box" ndarray is always the bottom-left point of the rectangle. For example, in the rectangle shown below, the first sub-list of "box" ndarray need not represent point A always.

rectangle

So here is what "box" values represent:

As the question rightly points out, when you print box, you will get a ndarray that looks something like this:

enter image description here

And then I went an extra mile for description and wrote this simple for loop to really understand what "box" values actually represent:

for i in box:
    cv2.circle(image,(i[0],i[1]), 3, (0,255,0), -1)
    imgplot = plt.imshow(image)
    plt.show()

And the results are: (the images are in order)

first image

enter image description here

enter image description here

enter image description here

I think the images should have cleared anybody's doubt about "box" values, but here is a summary anyway:

The lowest point of the rectangle(does not matter left or right) will always be the first sub-list of the "box" ndarray. So in the example I have given, the first sub-list [169 144] represents the "bottom right of this rectangle". Now this point will be the reference point to decide what the next sub-list represents. Meaning, the next sub-list will always represent the point that you first get when you move in the clockwise direction. (as shown in the second image of the for loop)

And keep moving in the clockwise direction to see what the next sub-lists represent.

PS: It is sometimes very hard to read the OpenCV documentation(which is not the best in the world btw) and understand a function and its return values properly. So I suggest churn up little chunks of code, like the for loop and cv2.circle above, to really visualize the return values of a function. That should really clear all your doubts about any functions that you come across in OpenCV. After all, OpenCV is all about "visual"izing!

Sushanth
  • 2,224
  • 13
  • 29
  • Can you please expand on how can we determine if the first point is the `bottom-right` or the `bottom-left` of the bounding box? "*the first sub-list [169 144] represents the bottom right of this rectangle*" **How did you determine this just by looking at the sub-list?** And can you suggest a way to determine this in code? – Sabito stands with Ukraine Sep 27 '20 at 09:28
  • 1
    To determine whether it is a bottom-left or right in a scenario when the two bottom points have the same y-coordinate: i. First, have a conditional statement to see whether the two bottom points have the same y-coordinate. ii. If the condition is True, then check which coordinate has the **lowest x-value**(or the highest). The coordinate with the lowest x-value willl be bottom-left of course! "How did you determine this just by looking at the sub-list?" -- I did not determine it by just looking at it! I could not! Which is exactly why I wrote the above for-loop in my answer! – Sushanth Sep 27 '20 at 18:52
  • My `cv2` version is **4.5.1**, in this version I find the lowest point will be the last sub-list element of array but stll move in clockwise direction. Hope for updating this anwser. – Ego ren Apr 07 '21 at 02:02
8

Those are the 4 points defining the rotated rectangle provided to it. Keep in mind that in opencv points are plotted as (x,y) not (row,column), and the y axis is positive downward. So the first point would be plotted 200 pixels right of the left side of the image and 472 pixels down from the top of the image. In other words, the first point is the bottom left point of the image.

afinit
  • 985
  • 1
  • 9
  • 16
4

Leaving this here for who -like me- finds this and reads the (current) most-voted answer: that now seems to be outdated.

Currently (using OpenCV 4.5.4, I don't know since when this is the case), the beaviour of cv.boxPoints() seems to match the behaviour of cv::RotatedRect::points(), i.e., th eorder of the returned points is: [bottom-left, top-left, top-right, bottom-right].

There is no explicit confirmation of this in the documetation, but the docs for cv.boxPoints() mention using directly cv::RotatedRect::points() in C++ and the following example shows that the solution by Sushanth seems to be wrong now (forgive the weird numbers, this comes directly out of the debugger in one of my projects):

rotrec = (
  (27.425756454467773, 947.3493041992188), # center pt
  (14.5321683883667, 50.921504974365234),  # W, H
  70.49755096435547                        # angle
)
cv2.boxPoints(rotrec)

output:

array([[  0.99999475, 949.0001    ],
       [ 48.999996  , 932.0001    ],
       [ 53.851517  , 945.6985    ],
       [  5.8515167 , 962.6985    ]], dtype=float32)

(note that the last point has a higher Y coordinate and should thus be the first point in the returned polygon, according to the algorithm described by Sushanth)

GPhilo
  • 18,519
  • 9
  • 63
  • 89
  • 1
    It took me quite a lot to figure out what the heck is going on with the rotated rects. I know there must be an update somewhere because errors started to appear after a numpy version update. Lesson learned to cover everything with tests. Point 0 looks to be the one most to the left. – Alex Saplacan Jun 29 '22 at 13:55
1

I think the first point will always be the bottom most points, and it will actually be the bottom right (if there are multiple points that could be the bottom most point).

Erotemic
  • 4,806
  • 4
  • 39
  • 80
0

As per version 4.7.0.68 of opencv-python (cv2) the return values (xy coordinates) of the cv2.boxPoints() method are in the following order (order is constant - it doesn't change):

  1. top-right
  2. top-left
  3. bottom-right
  4. bottom-left
tsveti_iko
  • 6,834
  • 3
  • 47
  • 39
-1

I was facing the same issue..

first of all the syntax should be boxPoints not BoxPoints

then, run the program with python3.. it fixed my issue

example, python3 test.py

kuro
  • 3,214
  • 3
  • 15
  • 31
Logesh P
  • 209
  • 4
  • 18
-2
<class 'numpy.ndarray'> 

you can find by type(elemnt you want to know)

It's strange that it is not in the documentation of the opencv python what the methods return

sivi
  • 10,654
  • 2
  • 52
  • 51