user3925023
user3925023

Reputation: 687

Python - OpenCV - return lowest bottom rectangle

I have a script which generate a HSV mask, and detect some rectangles based on area: enter image description here

I've already included some area limits, my goal is to detect only the lowest bottom box (the one in red in above picture).

This is my detecting code part where I'd like to add the new function capable to keep only 1 box:

cnts, hierarchy = cv2.findContours(mask_merged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
        p_box = round((cv2.contourArea(c)/image_area)*100,1)
        print('p_box'+str(p_box))
        if (p_box <= 2.2) and (p_box >= 0.7):
            (x, y, w, h) = cv2.boundingRect(c)
            print(x,y,w,h)
            print(cv2.contourArea(c))
            cv2.imshow('frame_ocr_zone',image_src[y:y+h, x:x+w])
            cv2.rectangle(image_src, (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_src,[box],0,(123, 245, 66),5)

            font = cv2.FONT_HERSHEY_SIMPLEX 
            color = (157,252,3) 
            thickness = 2
            fontScale = 1
            org = (x-50,y-20)

I was looking to code reported here: https://www.pyimagesearch.com/2015/04/20/sorting-contours-using-python-and-opencv/#pyi-pyimagesearch-plus-pricing-modal

I was thinking to modify the function here (sorting):

def sort_contours(cnts, method="left-to-right"):
    # initialize the reverse flag and sort index
    reverse = False
    i = 0
    # handle if we need to sort in reverse
    if method == "right-to-left" or method == "bottom-to-top":
        reverse = True
    # handle if we are sorting against the y-coordinate rather than
    # the x-coordinate of the bounding box
    if method == "top-to-bottom" or method == "bottom-to-top":
        i = 1
    # construct the list of bounding boxes and sort them from top to
    # bottom
    boundingBoxes = [cv2.boundingRect(c) for c in cnts]
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
        key=lambda b:b[1][i], reverse=reverse))
    # return the list of sorted contours and bounding boxes
    return (cnts, boundingBoxes)

But I cannot figure out on just retrieve 1 box (order top-to-bottom, and keep max of c?)

Thanks in advance for any suggestion.

###UPDATE I did found this post here: Python opencv sorting contours

This code is acually capable to do so:

def get_contour_precedence(contour, cols):
    tolerance_factor = 10
    origin = cv2.boundingRect(contour)
    return ((origin[1] // tolerance_factor) * tolerance_factor) * cols + origin[0]

im, contours, h = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

contours.sort(key=lambda x:get_contour_precedence(x, img.shape[1]))

Upvotes: 1

Views: 378

Answers (2)

user3925023
user3925023

Reputation: 687

def get_contour_precedence(contour, cols):
    tolerance_factor = 10
    origin = cv2.boundingRect(contour)
    return ((origin[1] // tolerance_factor) * tolerance_factor) * cols + origin[0]

im, contours, h = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

contours.sort(key=lambda x:get_contour_precedence(x, img.shape[1]))

Upvotes: 0

Sivaram Rasathurai
Sivaram Rasathurai

Reputation: 6333

First we define initial X, and Intial Y as 0 also we don't have a contour so that is none then we are looping over contours check the x coordinate and y coordinate since the bottom corner has x and y are greater than others

intial_x,intial_y = 0,0
my_cnt =None
for c in cnts:
(x, y, w, h) = cv2.boundingRect(c)
 if x>intial_x and y>intial_y:
   my_cnt = c
   intial_x = x+w
   intial_y =y+h
 else:
  continue

Upvotes: 1

Related Questions