Search code examples
pythonopencvdrawrectangle

Largest square in contact with most points in contour


I have an image, which I've found and drawn the contours from opencv (see left image).

I'd like to draw a box (something like the one on the right) as follows which should be in contact with the most amount of points from the contour.

What functions would be useful here? I thought about trying to find the largest rectangle but as you can see there are areas that sit inside the region so it would exclude that?

Many thanks for your help!

Diagram


Solution

  • First you need to transform the image into binary (black or white pixels) to differentiate between contour and the rest of the image (in the example the contour is white on a black background). Then you can "line-scan" the image, meaning to count the number of white pixel every row and every column. First horizontal edge (horizontal line with the most white pixels) of the rectangle should be in the first half of height of the image and the next horizontal edge in the second half. Like-wise for vertical edge (vertical line with the most white pixels), the first one should be in the first half of width of the image and the next one in the next. Go through the code below and it will become clear.

    Example code:

    import cv2
    import numpy as np
    
    img = cv2.imread("2.png")
    h, w = img.shape[:2]
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY_INV)[1]
    thresh = cv2.rectangle(thresh, (0, 0), (w, h), (0, 0, 0), 3)
    
    width = 1
    horizontal_line = []
    
    best_hor_one = 0
    best_hor_two = 0
    count_hor_one = 0
    count_hor_two = 0
    for y in range(h):
        area = thresh[y:y+width, 0:h]
        try:
            line = cv2.countNonZero(area)
            if line >= count_hor_one and y < h//2:
                best_hor_one = y
                count_hor_one = line
            if line >= count_hor_two and y >= h//2:
                best_hor_two = y
                count_hor_two = line
        except TypeError:
            pass
    
    best_ver_one = 0
    best_ver_two = 0
    count_ver_one = 0
    count_ver_two = 0
    for x in range(w):
        area = thresh[0:h, x:x+width]
        try:
            line = cv2.countNonZero(area)
            if line > count_ver_one and x < w//2:
                best_ver_one = x
                count_ver_one = line
            if line > count_ver_two and x >= w//2:
                best_ver_two = x
                count_ver_two = line
        except TypeError:
            pass
    
    cv2.line(img, (best_ver_one, best_hor_one),
            (best_ver_two, best_hor_one), (0, 255, 0), 3)
    cv2.line(img, (best_ver_one, best_hor_two),
            (best_ver_two, best_hor_two), (0, 255, 0), 3)
    cv2.line(img, (best_ver_one, best_hor_one),
            (best_ver_one, best_hor_two), (0, 255, 0), 3)
    cv2.line(img, (best_ver_two, best_hor_one),
            (best_ver_two, best_hor_two), (0, 255, 0), 3)
    
    cv2.imwrite("res.png", img)
    cv2.imshow("img", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    Result:

    enter image description here

    Input image:

    enter image description here