Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to "stretch" out a bounding box given from minAreaRect function in openCV?

I wish to run a line detector between two known points on an image but firstly I need to widen the area around the line so my line detector has more area to work with. The main issue it stretch the area around line with respect to the line slope. For instance: white line generated form two points with black bounding box.

I tried manualy manipulating the box array:

input_to_min_area = np.array([[660, 888], [653, 540]]) # this works instead of contour as an input to minAreaRect

rect = cv.minAreaRect(input_to_min_area)
box = cv.boxPoints(rect)
box[[0, 3], 0] += 20
box[[1, 2], 0] -= 20
box = np.int0(box)

cv.drawContours(self.images[0], [box], 0, (0, 255, 255), 2)

But that doesn't work for any line slope. From vertical to this angle everything is fine, but for the horizontal lines doesn't work.

What would be a simpler solution that works for any line slope?

like image 945
MPutak Avatar asked Sep 07 '25 21:09

MPutak


2 Answers

A minAreaRect() gives you a center point, the size of the rectangle, and an angle.

You could just add to the shorter side length of the rectangle. Then you have a description of a "wider rectangle". You can then do with it whatever you want, such as call boxPoints() on it.

padding = 42

rect = cv.minAreaRect(input_to_min_area)

(center, (w,h), angle) = rect # take it apart

if w < h: # we don't know which side is longer, add to shorter side
    w += padding
else:
    h += padding

rect = (center, (w,h), angle) # rebuild

A box around your two endpoints, widened:

enter image description here

like image 63
Christoph Rackwitz Avatar answered Sep 10 '25 14:09

Christoph Rackwitz


We may add the padding in the axis that is perpendicular to the angle of the " minAreaRect".

  • Get the angle, and convert to radians

     angle = np.deg2rad(rect[2])  # Angle of minAreaRect
    
  • Padding in each direction is perpendicular to the angle of minAreaRect

     pad_x = 20*np.sin(angle)
     pad_y = 20*np.cos(angle)
    
  • Add the padding in both axes:
    Assume the order of the point in box is sorted according to the angle of rect (I don't know if it's always true - sorting the points may be required).

     box[[0, 3], 0] -= pad_x
     box[[1, 2], 0] += pad_x
     box[[0, 3], 1] += pad_y
     box[[1, 2], 1] -= pad_y
     box = np.int0(box)
    

Code sample:

import cv2
import numpy as np

img = cv2.imread('sketch.png')

#input_to_min_area = np.array([[584, 147], [587, 502]]) # this works instead of contour as an input to minAreaRect
#input_to_min_area = np.array([[109, 515], [585, 144]]) # this works instead of contour as an input to minAreaRect
input_to_min_area = np.array([[80, 103], [590, 502]]) # this works instead of contour as an input to minAreaRect

rect = cv2.minAreaRect(input_to_min_area)
box = cv2.boxPoints(rect)

angle = np.deg2rad(rect[2])  # Angle of minAreaRect

# Padding in each direction is perpendicular to the angle of minAreaRect
pad_x = 20*np.sin(angle)
pad_y = 20*np.cos(angle)

box[[0, 3], 0] -= pad_x
box[[1, 2], 0] += pad_x
box[[0, 3], 1] += pad_y
box[[1, 2], 1] -= pad_y
box = np.int0(box)

cv2.drawContours(img, [box], 0, (0, 255, 255), 2)

cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()

Sample output:

enter image description here


We may also want to expand the box in the parallel direction.
I am still not sure about the signs...

In this case it's simpler to update input_to_min_area:

import cv2
import numpy as np

img = cv2.imread('sketch.png')

#input_to_min_area = np.array([[584, 147], [587, 502]]) # this works instead of contour as an input to minAreaRect
#input_to_min_area = np.array([[109, 515], [585, 144]]) # this works instead of contour as an input to minAreaRect
input_to_min_area = np.array([[80, 103], [590, 502]]) # this works instead of contour as an input to minAreaRect

rect = cv2.minAreaRect(input_to_min_area)

angle = np.deg2rad(rect[2])  # Angle of minAreaRect
pad_x = int(round(20*np.cos(angle)))
pad_y = int(round(20*np.sin(angle)))
tmp_to_min_area = np.array([[input_to_min_area[0, 0]+pad_x, input_to_min_area[0, 1]+pad_y], [input_to_min_area[1, 0]-pad_x, input_to_min_area[1, 1]-pad_y]])
rect = cv2.minAreaRect(tmp_to_min_area)

box = cv2.boxPoints(rect)

angle = np.deg2rad(rect[2])  # Angle of minAreaRect

# Padding in each direction is perpendicular to the angle of minAreaRect
pad_x = 20*np.sin(angle)
pad_y = 20*np.cos(angle)

box[[0, 3], 0] -= pad_x
box[[1, 2], 0] += pad_x
box[[0, 3], 1] += pad_y
box[[1, 2], 1] -= pad_y

box = np.int0(box)

cv2.drawContours(img, [box], 0, (0, 255, 255), 2)

cv2.line(img, (tmp_to_min_area[0, 0], tmp_to_min_area[0, 1]), (tmp_to_min_area[1, 0], tmp_to_min_area[1, 1]), (255, 0, 0), 2)

cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()

Output:
enter image description here

like image 38
Rotem Avatar answered Sep 10 '25 12:09

Rotem