pythonopencvomr

OpenCV find the contours of box based on 4 square points around


Is there a way to get content inside the box (dashed red rectangle I've noted) based on 4 small squares around it?

Declare

My code:

# Read and resize image
img_input = cv2.imread(args.pop('image'), )
img_input_height, img_input_width, _ = img_input.shape
img_input_ap = img_input_width / img_input_height
img_input_width = int(const_image_max_height * img_input_ap)
img_input = cv2.resize(img_input, (img_input_width, img_input_height,), )

# Process image color
img_final = img_input.copy()
img_gray = cv2.cvtColor(img_input, cv2.COLOR_BGR2GRAY, )
img_blur = cv2.GaussianBlur(img_gray, (5, 5,), 5)
img_canny = cv2.Canny(img_blur, 10, 70, )

# Find around contours
img_canny_contours, _ = cv2.findContours(
   img_canny,
   cv2.RETR_EXTERNAL,
   cv2.CHAIN_APPROX_NONE, 
)

# Find square contours
img_preview = img_final.copy()
for contour in img_canny_contours:
    rect = cv2.boundingRect(contour)
    rect_width = rect[2]
    rect_height = rect[3]
    if 0.8 <= (rect_width / rect_height) <= 1.2:
        # Now I founded the squares...

# Now I need to find the rectangle between 4 squares founded here...

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

Current result:enter image description here


Solution

  • One way, considering that dashed line is not supported by OpenCV drawing functions.

    First read the image in grayscale, blur and threshold:

    blurred = cv2.GaussianBlur(im, (5, 5), 0)
    threshold = 200
    apply_val = 255
    ret, th = cv2.threshold(blurred, threshold, apply_val, cv2.THRESH_BINARY)
    

    Use findContorus function with cv2.RETR_CCOMP as retrieval mode:

    contours, hierarchy = cv2.findContours(th, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
    

    Recover the vertex as center of the contours, skipping the first which is the image boundary:

    vertex = []
    for c in contours[1:]:
        moment = cv2.moments(c)
        cx = int(moment["m10"] / moment["m00"])
        cy = int(moment["m01"] / moment["m00"])
        vertex.append((cx, cy))
    

    Now you need to sort the vertex, you can define a method that does, this is manual:

    sorted_vertex = [vertex[1], vertex[0], vertex[2], vertex[3]]
    vertex_rotated = sorted_vertex[1:] + sorted_vertex[:1]
    vertex_pairs = list(zip(sorted_vertex, vertex_rotated))
    

    You can draw a poly using sorted_vertex or a sequence of lines using vertex_pairs.

    res = np.ones_like(im) * 255
    res = cv2.cvtColor(res2, cv2.COLOR_GRAY2BGR)
    
    for a, b in vertex_pairs:
        cv2.line(res2, a, b, (0, 0, 255), 5)
    

    Getting this result (Look on SO for a way to draw a dashed line):

    enter image description here