我正在尝试使用 opencv 和 python 的视频源来检测各种形状,但是我的代码只检测到“鬼圈”,我不知道为什么会发生这种行为。我在我的代码中使用这个视频。
这里我的代码完整评论:
import cv2
# Initialize counters for detected shapes
triangle_count = 0
quadrilateral_count = 0
pentagon_count = 0
hexagon_count = 0
circle_count = 0
# Horizontal reference line (middle of the frame)
line_y = 240 # Adjust according to the height of the video
# Read the video from file
video_path = 'video.mp4' # Video path
cap = cv2.VideoCapture(video_path)
# Check if the video is loaded correctly
if not cap.isOpened():
print("Error opening video.")
exit()
# Process the video frame by frame
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break # Exit when the video ends
# Create a copy of the original frame to use later
original = frame.copy()
# Convert the frame from BGR to HSV
hsv_image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# Convert the HSV image to grayscale
gray_image = cv2.cvtColor(hsv_image, cv2.COLOR_BGR2GRAY)
# Apply Otsu thresholding to binarize the image
ret, otsu = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Apply the mask to the original image
image = cv2.bitwise_and(original, original, mask=otsu)
# Find contours in the binary image
contours, _ = cv2.findContours(otsu, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Draw the horizontal reference line
cv2.line(frame, (0, line_y), (frame.shape[1], line_y), (0, 255, 0), 2)
# Process each contour
for i, contour in enumerate(contours):
if i == 0: # Ignore the largest outer contour
continue
# Calculate the area of the contour
contour_area = cv2.contourArea(contour)
# Filter out small objects based on area
if contour_area < 300: # Adjust the minimum area value
continue
# Approximate the shape of the contour
epsilon = 0.01 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
# Calculate the center of the object (bounding box coordinates)
x, y, w, h = cv2.boundingRect(approx)
center_y = y + h // 2 # Y coordinate of the object's center
# Check if the object crosses the horizontal reference line
if line_y - 10 <= center_y <= line_y + 10:
# Classify the shape based on the number of vertices
if len(approx) == 3:
cv2.putText(frame, "Triangle", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
triangle_count += 1
elif len(approx) == 4:
cv2.putText(frame, "Quadrilateral", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
quadrilateral_count += 1
elif len(approx) == 5:
cv2.putText(frame, "Pentagon", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
pentagon_count += 1
elif len(approx) == 6:
cv2.putText(frame, "Hexagon", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
hexagon_count += 1
else:
cv2.putText(frame, "Circle", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 0), 2)
circle_count += 1
# Draw the detected contour
cv2.drawContours(frame, [approx], 0, (0, 0, 0), 2)
# Display the counters in the top left corner
cv2.putText(frame, f"Triangles: {triangle_count}", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
cv2.putText(frame, f"Quadrilaterals: {quadrilateral_count}", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
cv2.putText(frame, f"Pentagons: {pentagon_count}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
cv2.putText(frame, f"Hexagons: {hexagon_count}", (10, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
cv2.putText(frame, f"Circles: {circle_count}", (10, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 0), 2)
# Show the processed frame
cv2.imshow("Shape Detection", frame)
# Exit with the 'q' key
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Release resources
cap.release()
cv2.destroyAllWindows()
正如我们所见,当形状穿过水平线时应该进行分类。如果你们能帮我解决这个问题,我将不胜感激,上面是测试我的代码的视频链接,只需要在您的机器上安装opencv和python。
更新 - 第二种方法。
这个形状的面积似乎> 5000
代码:
import math
import numpy as np
import cv2
# Initialize the camera or video
cap = cv2.VideoCapture("video.mp4")
print("Press 'q' to exit")
# Function to calculate the angle between three points
def angle(pt1, pt2, pt0):
dx1 = pt1[0][0] - pt0[0][0]
dy1 = pt1[0][1] - pt0[0][1]
dx2 = pt2[0][0] - pt0[0][0]
dy2 = pt2[0][1] - pt0[0][1]
return float((dx1 * dx2 + dy1 * dy2)) / math.sqrt(float((dx1 * dx1 + dy1 * dy1)) * (dx2 * dx2 + dy2 * dy2) + 1e-10)
# Initialize a dictionary to count the detected shapes
shape_counts = {
'TRI': 0,
'RECT': 0,
'PENTA': 0,
'HEXA': 0,
'CIRC': 0
}
# Main loop
while(cap.isOpened()):
# Capture frame by frame
ret, frame = cap.read()
if ret:
# Convert to grayscale
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Apply the Canny detector
canny = cv2.Canny(gray, 80, 240, 3)
# Find contours
contours, hierarchy = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Draw a horizontal line in the center of the image
line_y = int(frame.shape[0] / 2)
cv2.line(frame, (0, line_y), (frame.shape[1], line_y), (0, 255, 0), 2)
# Shape detection counter
for i in range(len(contours)):
# Approximate the contour with precision proportional to the perimeter of the contour
approx = cv2.approxPolyDP(contours[i], cv2.arcLength(contours[i], True) * 0.02, True)
#print(cv2.contourArea(contours[i]))
# Filter small or non-convex objects
if abs(cv2.contourArea(contours[i])) < 5000 or not cv2.isContourConvex(approx):
continue
# Classify the shapes based on the number of vertices
x, y, w, h = cv2.boundingRect(contours[i])
if y + h / 2 > line_y: # Only classify if the shape crosses the line
if len(approx) == 3:
# Triangle
shape_counts['TRI'] += 1
cv2.putText(frame, 'TRI', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2, cv2.LINE_AA)
elif 4 <= len(approx) <= 6:
# Polygon classification
vtc = len(approx)
cos = []
# Calculate the angles between the vertices using the angle() function
for j in range(2, vtc + 1):
cos.append(angle(approx[j % vtc], approx[j - 2], approx[j - 1]))
# Sort the angles and determine the type of figure
cos.sort()
mincos = cos[0]
maxcos = cos[-1]
# Classify based on the number of vertices
if vtc == 4:
shape_counts['RECT'] += 1
cv2.putText(frame, 'RECT', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2, cv2.LINE_AA)
elif vtc == 5:
shape_counts['PENTA'] += 1
cv2.putText(frame, 'PENTA', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2, cv2.LINE_AA)
elif vtc == 6:
shape_counts['HEXA'] += 1
cv2.putText(frame, 'HEXA', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2, cv2.LINE_AA)
else:
# Detect and label circle
area = cv2.contourArea(contours[i])
radius = w / 2
if abs(1 - (float(w) / h)) <= 2 and abs(1 - (area / (math.pi * radius * radius))) <= 0.2:
shape_counts['CIRC'] += 1
cv2.putText(frame, 'CIRC', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2, cv2.LINE_AA)
# Display the number of each detected shape at the top of the image
offset_y = 30
for shape, count in shape_counts.items():
cv2.putText(frame, f'{shape}: {count}', (10, offset_y), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2, cv2.LINE_AA)
offset_y += 30
# Display the resulting frame
cv2.imshow('Frame', frame)
cv2.imshow('Canny', canny)
# Exit if 'q' is pressed
if cv2.waitKey(1) == ord('q'):
break
# Once finished, release the capture
cap.release()
cv2.destroyAllWindows()
但是检测到许多矩形和五边形,但我的视频只有三角形和矩形/正方形。似乎我只需要对大于 5000 的形状进行分类,并尝试关闭他的顶点,因为有时轮廓不完整。
提前致谢。
我正在尝试使用 opencv 使用视频源来检测各种形状 使用Python,但是我的代码只检测“幽灵圈”,
问题可以解决。
我看到你的影子(拿着摄像机)在几何形状后面和影子一起。 盒子和三角形不是图像。
cv2.putText
命令。epsilon = 0.09 * cv2.arcLength(contour, True)
#epsilon = 0.01
cv2.drawContours 在 if\elses
条件块内修改脚本片段:
import cv2
# Initialize counters for detected shapes
triangle_count = 0
quadrilateral_count = 0
pentagon_count = 0
hexagon_count = 0
circle_count = 0
# Horizontal reference line (middle of the frame)
line_y = 240 # Adjust according to the height of the video
# Read the video from file
video_path = 'V0.mp4' # Video path
cap = cv2.VideoCapture(video_path)
# Check if the video is loaded correctly
if not cap.isOpened():
print("Error opening video.")
exit()
# Process the video frame by frame
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break # Exit when the video ends
# Create a copy of the original frame to use later
original = frame.copy()
# Convert the frame from BGR to HSV
#hsv_image = cv2.cvtColor(dst, cv2.COLOR_BGR2HSV)
# Convert the HSV image to grayscale
gray_image = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
# Apply Otsu thresholding to binarize the image
ret, otsu = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Apply the mask to the original image
# Find contours in the binary image
contours, _ = cv2.findContours(otsu, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Draw the horizontal reference line
cv2.line(frame, (0, line_y), (frame.shape[1], line_y), (0, 255, 0), 2)
# Process each contour
for i, contour in enumerate(contours):
if i == 0: # Ignore the largest outer contour
continue
# Calculate the area of the contour
contour_area = cv2.contourArea(contour)
# Filter out small objects based on area
if contour_area <= 300: # Adjust the minimum area value
continue
# Approximate the shape of the contour
epsilon = 0.09 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
# Calculate the center of the object (bounding box coordinates)
x, y, w, h = cv2.boundingRect(approx)
#cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
center_y = y + h // 2 # Y coordinate of the object's center
cv2.drawContours(frame, [approx], 0, (0, 255, 0), 2)
# Check if the object crosses the horizontal reference line
if line_y - 10 <= center_y <= line_y + 10:
# Classify the shape based on the number of vertices
if len(approx) == 3:
triangle_count += 1
#cv2.putText(frame, "Triangle", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
txt = "Triangle"
elif len(approx) == 4:
quadrilateral_count += 1
txt = "Quadrilateral"
#cv2.putText(frame, "Quadrilateral", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
elif len(approx) == 5:
pentagon_count += 1
txt = "Pentagon"
#cv2.putText(frame, "Pentagon", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
elif len(approx) == 6:
hexagon_count += 1
txt = "Hexagon"
#cv2.putText(frame, "Hexagon", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
elif len(approx) == 0:
circle_count += 1
txt = "Circle"
#cv2.putText(frame, "Circle", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
cv2.putText(frame, f'{str(txt)}', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
# Display the counters in the top left corner
cv2.putText(frame, f'Triangles: {str(triangle_count)}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
cv2.putText(frame, f"Quadrilaterals: {quadrilateral_count}", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
cv2.putText(frame, f"Pentagons: {pentagon_count}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
cv2.putText(frame, f"Hexagons: {hexagon_count}", (10, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
cv2.putText(frame, f"Circles: {circle_count}", (10, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 0), 2)
# Show the processed frame
cv2.imshow("Shape Detection", frame)
# Exit with the 'q' key
if cv2.waitKey(72) & 0xFF == ord('q'):
break
# Release resources
cap.release()
cv2.destroyAllWindows()
截图: