I'm struggling to get the bounding box for an identified area in an image. The following code results in this image: .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):
Here is the original image:
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()
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/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):
Here is the original image:
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()
Share
Improve this question
edited Feb 17 at 13:03
Patrick Plaatje
asked Feb 16 at 11:02
Patrick PlaatjePatrick Plaatje
1091 silver badge9 bronze badges
3
- something that i noticed just now, please add the source image, without any of the drawings – Tino D Commented Feb 17 at 11:13
- @ChristophRackwitz apologies, how can I avoid that? – Patrick Plaatje Commented Feb 17 at 13:02
- @TinoD I have added the source image. – Patrick Plaatje Commented Feb 17 at 19:53
1 Answer
Reset to default 2To 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])