I have a task to detect a shooting target with OpenCv. The shooting target is a normal shooting target, the problem is the camera is located at the bottom of the target, so that the image will be angled.
I already crop and change the perspective of the image with OpenCv findhomograph. But I changed the perspective manually because I cannot create a code to detect the shooting target automatically.
Changed Perspective Shooting Target
The result of the changed perspective are not exactly circle, but it is a little bit ellipse and blurry. The OpenCv code that I tried cannot detect the outer circle because the image is blurred
This is my code that I already tried
original_image = cv2.imread("ImageLocation")
original_height, original_width = original_image.shape[:2]
# Set the desired display width
display_width = 1300
# Calculate the corresponding display height while maintaining aspect ratio
aspect_ratio = original_width / original_height
display_height = int(display_width / aspect_ratio)
# Resize the image
resized_image = cv2.resize(original_image, (display_width, display_height))
# Manually define the original corners (clockwise order)
original_corners = np.array([[465, 13], [882, 3], [218, 580], [1145, 583]], dtype=np.float32)
original_corners = original_corners.astype(np.int32)
# Draw the manually defined corners on the original image (blue color)
for corner in original_corners:
cv2.circle(resized_image, tuple(corner), 1, (255, 0, 0), -1)
target_width = 413 # Adjust this based on your desired output size
target_height = 625
new_corners = np.array([[465, 3], [882, 3], [463, 580], [882, 580]], dtype=np.float32)
# Calculate the perspective transformation matrix
original_perspective_matrix, _ = cv2.findHomography(original_corners.astype(np.float32), new_corners)
# inverse_perspective_matrix = cv2.getPerspectiveTransform(new_corners, original_corners.astype(np.float32))
# Apply the perspective transformation
corrected_image = cv2.warpPerspective(resized_image, original_perspective_matrix, (0, 0))
crop_x1, crop_y1 = 465, 3 # Top-left corner
crop_x2, crop_y2 = 882, 580 # Bottom-right corner
# Crop the transformed image
cropped_image = corrected_image[crop_y1:crop_y2, crop_x1:crop_x2]
cropped_height, cropped_width = cropped_image.shape[:2]
brightened_image = cv2.subtract(cropped_image, 200)
# Display the corrected image with original corners and main contour
cv2.imshow("Original Image with Manual Corners", resized_image)
cv2.imshow("Corrected Image with Original Corners and Main Contour", corrected_image)
cv2.imshow("cropped Image", cropped_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# hsv
hsv = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
# find the outer ring
lower_brightness = 70 # Adjust this value as needed
upper_brightness = 216 # Adjust this value as needed
# Extract the Value channel from the HSV image
value_channel = hsv[:, :, 2] # Value channel is the third channel
# Create a mask for the specified brightness range
brightness_mask = cv2.inRange(value_channel, lower_brightness, upper_brightness)
# Apply the mask to the original image to show only pixels within the brightness range
filtered_result = cv2.bitwise_and(cropped_image, cropped_image, mask=brightness_mask)
gray_filtered = cv2.cvtColor(filtered_result, cv2.COLOR_BGR2GRAY)
cv2.imshow('vmask', gray_filtered)
# contours
contours, _ = cv2.findContours(gray_filtered, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
circle_centers = []
for contour in contours:
# Calculate the center of each bullet hole
(x, y, w, h) = cv2.boundingRect(contour)
if w > 25:
center_x = x + w // 2
center_y = y + h // 2
radius = max(w, h) // 2
circle_centers.append((center_x, center_y, radius))
The result of the code above is like this.
Masked Shooting Target with HSV
I want to change the homograph of the image to exactly circle, not an ellipse so that the OpenCv can detect each circle. I also want the outer circle to be detected.
Have you tried using threshold image? Please try these codes below
I think you can use these code to detect the shooting range