I have binary masks obtained from segmentation model, and I want to get four corners only of its contour that include majority of the points with minimal area as you can see in this image:
The corners are not necessarily forming a rectangle, and the mask is noisy:
I have tried contour detection, approximation, and minAreaRect
, but the rectangle is in many cases wider than the shape, and not minimal to the limit I want:
#!/usr/bin/env python3
import os
from os import path as osp
import cv2
import numpy as np
path = "seg_masks"
im_list = os.listdir(path)
def lcc(binary_image:np.ndarray)->np.ndarray:
# Find connected components
print(binary_image.shape, binary_image.dtype)
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary_image, connectivity=8)
# Find the label (index) of the largest connected component
largest_component_label = np.argmax(stats[1:, cv2.CC_STAT_AREA]) + 1 # Skip background label (0)
largest_component_mask = (labels == largest_component_label).astype(np.uint8)
largest_component_mask = largest_component_mask.astype(np.uint8)
return largest_component_mask
for img_name in im_list:
bgr_img_mask = cv2.imread(osp.join(path, img_name), 0)
cv2.imwrite(osp.join(path, "white", img_name), bgr_img_mask)
lcc_mask = lcc(bgr_img_mask)
# Erosion to clean the mask contour a bit
cl_ker = 5
kernel = np.ones((cl_ker, cl_ker), np.uint8)
erosion = cv2.erode(lcc_mask,kernel,iterations = 3)
contours, _ = cv2.findContours(
lcc_mask, mode=cv2.RETR_TREE,
method=cv2.CHAIN_APPROX_NONE
)
if(len(contours)):
max_cnt = max(contours, key=cv2.contourArea)
epsilon = 0.008 * cv2.arcLength(max_cnt, True)
approx = cv2.approxPolyDP(max_cnt, epsilon, True)
approx = np.squeeze(np.array(approx, dtype=int), axis=1)
rect = cv2.minAreaRect(approx)
box = cv2.boxPoints(rect)
box = np.int0(box)
bgr_img_mask = cv2.drawContours(bgr_img_mask, [box], 0, 200, 2)
cv2.drawContours(bgr_img_mask, [approx], -1, 128, 2)
bgr_img_mask = cv2.putText(bgr_img_mask, f"{round(epsilon,2)}", (50, 50) ,
cv2.FONT_HERSHEY_SIMPLEX , 1, 255, 2, cv2.LINE_AA)
win_name = "img"
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
cv2.imshow(win_name, bgr_img_mask)
cv2.waitKey(0)
产出:
您能否指导我如何做到这一点? 感谢。