Patrick Plaatje
Patrick Plaatje

Reputation: 109

Drawing a bounding box around the largest contour python CV

I'm struggling to get the bounding box for an identified area in an image. The following code results in this image: https://i.sstatic.net/6HX4dNrB.png. However, as might be obvious from the picture, I'd like to bounding box to enclose the (black) identified large area instead. Any suggestions on how to accomplish that?

The following image shows what the desired result should be (the red contours and the thick green bounding box): enter image description here

Here is the original image: enter image description here

import numpy as np
import cv2

# Load the image
image = cv2.imread("image.png")  # Update with your image path
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# Define refined HSV ranges for sandy ground (light brown, beige, gray)
lower_sand = np.array([10, 20, 100])  # Lower bound for sand-like colors
upper_sand = np.array([30, 100, 255])  # Upper bound for sand-like colors

# Create a mask for the sandy competition ground
mask_sand = cv2.inRange(hsv, lower_sand, upper_sand)

# Apply morphological operations to reduce noise
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
mask_sand = cv2.morphologyEx(mask_sand, cv2.MORPH_CLOSE, kernel)  # Close gaps
mask_sand = cv2.morphologyEx(mask_sand, cv2.MORPH_OPEN, kernel)   # Remove small noise

# Invert the mask (to highlight everything except the competition area)
mask = cv2.bitwise_not(mask_sand)

# Apply the inverted mask to the original image
output = cv2.bitwise_and(image, image, mask=mask)

# Ensure the mask is properly binarized for contour detection
ret, thresh = cv2.threshold(mask, 50, 255, cv2.THRESH_BINARY)

# Find contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

if contours:
    # Find the largest contour by area
    largest_contour = max(contours, key=cv2.contourArea)

    # Approximate the contour to a quadrilateral
    epsilon = 0.02 * cv2.arcLength(largest_contour, True)
    approx = cv2.approxPolyDP(largest_contour, epsilon, True)

    # If the approximation has 4 points, use it; otherwise, use convex hull
    if len(approx) == 4:
        quadrilateral = approx
    else:
        quadrilateral = cv2.convexHull(largest_contour)

    # Draw the quadrilateral in green
    cv2.polylines(output, [quadrilateral], isClosed=True, color=(0, 255, 0), thickness=3)

    # Draw all detected contours in blue
    cv2.drawContours(output, contours, -1, (255, 0, 0), 2)


# Show and save the output
cv2.imshow("Result", np.hstack([image, output]))
cv2.imwrite("output_image.png", output)

cv2.waitKey(0)
cv2.destroyAllWindows()

Upvotes: 1

Views: 86

Answers (1)

fmw42
fmw42

Reputation: 53154

To find the largest contour in Python/OpenCV, try

big_contour = max(contours, key=cv2.contourArea)

Then to get and draw the bounding box:

x1,y1,w1,h1 = cv2.boundingRect(big_contour)
cv2.rectangle(result, (x1, y1), (x1+w1, y1+h1), (0, 0, 255), 2)

If you are trying to draw color on a grayscale or binary image, you need to convert that to BGR before drawing. You can do that with cv2.cvtColor(output, cv2.COLOR_Gray2BGR) or cv2.merge([output, output, output])

Upvotes: 2

Related Questions