[Python] cv2.findContours fails to detect black rectangle aligned with x-axis

Discussão em 'Python' iniciado por Stack, Maio 10, 2025.

  1. Stack

    Stack Membro Participativo

    I'm working with OpenCV in Python to detect a large black rectangle in an image. Everything works fine when the rectangle is rotated at an angle, but when it's aligned exactly with the x-axis (i.e., perfectly horizontal), it doesn't get detected as a contour at all. As a result, cv2.minAreaRect() never even runs because there’s no contour to process.

    I've tried converting the image to grayscale and applying both thresholding and edge detection with cv2.Canny(). Visually, the rectangle is clearly present and distinct from the background. I'm also using cv2.findContours() with the RETR_EXTERNAL and CHAIN_APPROX_SIMPLE flags.

    This issue only occurs when the rectangle is perfectly horizontal. If I rotate the same shape slightly, it gets detected correctly. I suspect it might have something to do with how OpenCV approximates contours or handles horizontal/vertical edges.

    Has anyone run into this before? I’d really appreciate any insights into why this happens and how to make findContours more reliable for this case. Could it be related to resolution, anti-aliasing, or internal filtering?

    Thanks in advance for any help — I shared a code snippet if that helps!

    def detect_tunnel(image, aruco, binary_matrix, grid_size=5):
    # Convert image to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Adaptive thresholding
    thresh = cv2.adaptiveThreshold(
    gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 5
    )

    # Morphological operations to remove noise
    kernel = np.ones((5, 5), np.uint8)
    cleaned = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)

    # Find contours of the cleaned image
    contours, _ = cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # Filter out small contours based on a minimum area
    min_area = 10000 # Adjust based on image size
    large_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area]
    length = len(large_contours)
    print('length',length)
    for cnt in large_contours:

    image_copy=image.copy()
    cv2.drawContours(image_copy, cnt, 0, (0, 255, 0), 3)
    cv2.imshow("Tunnel large", image_copy)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    #more than one contour detected
    if length != 0:
    # Initialize variables for tunnel detection
    tunnel = None

    tunnel_cand=[]
    # Search for the best candidate rectangle representing the tunnel
    for contour in large_contours:
    rect = cv2.minAreaRect(contour) # Gets a rotated rectangle
    center, (width, height), angle = rect
    area = width * height
    aspect_ratio = max(width, height) / min(width, height)

    #test which rectangles are captured
    box = cv2.boxPoints(rect)
    box = np.intp(box)
    image_copy1 = image.copy()
    cv2.drawContours(image_copy1, [box], 0, (0, 255, 0), 3)
    cv2.imshow("Tunnel contour", image_copy1)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    #filter the contours for black pixels
    # Create a mask for the rotated rectangle
    mask = np.zeros(binary_matrix.shape, dtype=np.uint8)
    cv2.fillPoly(mask, [box // grid_size], 1)

    # Calculate black pixel ratio inside the mask
    masked_black = binary_matrix[mask == 1]
    if masked_black.size == 0:
    return False
    black_ratio = np.sum(masked_black == 1) / masked_black.size
    print('black_ratio',black_ratio)
    if black_ratio > 0.6:
    tunnel_cand.append(contour)
    print('ik heb iter')
    image_copy2=image.copy()
    cv2.drawContours(image_copy2, [box], 0, (0, 255, 0), 3)
    cv2.imshow("Tunnel zwart", image_copy2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    Continue reading...

Compartilhe esta Página