Search code examples
pythonopencvtracking

Drawing angled rectangles in OpenCV


I am using OpenCV and python to work on a project that involves body tracking, and I am using HSV values to find a skin tone then draw a box around it.

However although I can find the tracked object and draw a box around it the rectangles are always vertical, and I would like to know if there is anyway angle the rectangles so they better show the detected object, somewhat like the minEnclosingCircle function, but using a rectangle

The images probably explain what I am looking for better. The boxes that I am getting are green, and what I'm looking for I have drawn on in yellow. As you can see the mask shows and angled rectangle would also better encompass the selected area. I have also included the original image.

My code is:

import numpy as np
import cv2

# Input image
image = cv2.imread('TestIn.png')

# Converts to grey for better reulsts
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Converts to HSV
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# HSV values
lower_skin = np.array([5,36,53])
upper_skin = np.array([19,120,125])

mask = cv2.inRange(hsv, lower_skin, upper_skin)

mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)

# Finds contours
im2, cnts, hierarchy = cv2.findContours(mask.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Draws contours
for c in cnts:
    if cv2.contourArea(c) < 3000:
        continue

    (x, y, w, h) = cv2.boundingRect(c)
    cv2.rectangle(image, (x,y), (x+w,y+h), (0, 255, 0), 2)

cv2.imshow('mask', mask)
cv2.imshow('image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Input image:

Input Image

Output image (output boxes in green, desired boxes in yellow):

output image. output boxes in green, desired boxes in yellow


Solution

  • You need to use cv2.minAreaRect(...) and then cv2.boxPoints(...) to obtain a sequence of points representing the polygon in a format that can be used by other OpenCV drawing functions, such as cv2.drawContours(...) or cv2.polylines(...).


    Based on the example in OpenCV documentation I added few statements to your code to achieve the desired result:

    import numpy as np
    import cv2
    
    # Input image
    image = cv2.imread('oaHUs.jpg')
    
    # Converts to grey for better reulsts
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # Converts to HSV
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    
    # HSV values
    lower_skin = np.array([5,36,53])
    upper_skin = np.array([19,120,125])
    
    mask = cv2.inRange(hsv, lower_skin, upper_skin)
    
    mask = cv2.erode(mask, None, iterations=2)
    mask = cv2.dilate(mask, None, iterations=2)
    
    # Finds contours
    im2, cnts, hierarchy = cv2.findContours(mask.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
    # Draws contours
    for c in cnts:
        if cv2.contourArea(c) < 3000:
            continue
    
        (x, y, w, h) = cv2.boundingRect(c)
        cv2.rectangle(image, (x,y), (x+w,y+h), (0, 255, 0), 2)
    
        ## BEGIN - draw rotated rectangle
        rect = cv2.minAreaRect(c)
        box = cv2.boxPoints(rect)
        box = np.int0(box)
        cv2.drawContours(image,[box],0,(0,191,255),2)
        ## END - draw rotated rectangle
    
    cv2.imwrite('out.png', image)
    

    Output: