YOLOv8实现物体静止时长统计时出现AttributeError错误求助
嘿,别担心,我来帮你排查这个问题!
首先看你遇到的AttributeError: 'list' object has no attribute 'xyxy',这个错误的根源很明确:
核心问题:YOLOv8推理结果的格式理解错了
当你调用results = model(frame)时,返回的是一个包含Results对象的列表——哪怕你只传入了单张帧,它也会把结果包装成列表返回。而你直接把这个列表当成单个Results对象来调用.xyxy属性,列表当然没有这个属性,所以就报错了。
快速修复方案
你只需要调整推理结果的获取方式,两种方法选其一:
方法1:直接取列表中的第一个结果(推荐)
把推理那行代码改成:
# 取出列表中对应当前帧的Results对象 results = model(frame)[0]
然后你的bboxes处理代码就可以正常工作了:
# Get the bounding boxes of detected objects bboxes = results.xyxy[0].cpu().numpy()[:, :4] if len(results.xyxy) else []
方法2:在处理bboxes时先取第一个结果
如果不想改推理行,就修改bboxes的处理逻辑:
# Perform object detection on the current frame results = model(frame) # 先取出列表中的第一个Results对象 result = results[0] # Get the bounding boxes of detected objects bboxes = result.xyxy[0].cpu().numpy()[:, :4] if len(result.xyxy) else []
顺带帮你排查其他潜在Bug
看了你的代码,还有几个地方会导致后续报错,提前帮你指出来:
is_stopped变量未初始化
你代码里用到了is_stopped但开头没定义,会触发NameError,要在变量初始化部分加上:is_stopped = False # 初始化静止状态标记output_video未初始化
你直接调用output_video.write(frame)但没创建这个视频写入器,要在cap = cv2.VideoCapture(...)之后添加:# 初始化视频写入器 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) output_video = cv2.VideoWriter('output_idle_track.mp4', cv2.VideoWriter_fourcc(*'mp4v'), frame_rate, (frame_width, frame_height))output_directory未定义
你用到了output_directory但没创建/初始化,要在开头加上:output_directory = "idle_snapshots" os.makedirs(output_directory, exist_ok=True) # 自动创建目录,不存在则新建多目标跟踪的潜在问题
如果视频里有多个老鼠,你的代码会把最后一个bbox的坐标作为prev_x, prev_y,导致跟踪混乱。如果是单目标场景,建议只处理第一个检测到的目标:if len(bboxes) > 0: # 只取第一个检测到的目标(单目标跟踪) bbox = bboxes[0] (x_min, y_min, x_max, y_max) = bbox.astype(int) # 后续的位置判断、绘图逻辑都基于这个bbox
修正后的关键代码片段示例
import cv2 from ultralytics import YOLO import os import time from datetime import datetime # Load the model model_path = "YOLOv8/model/path" model = YOLO(model_path) # Open video cap = cv2.VideoCapture('target/video/path') frame_rate = cap.get(cv2.CAP_PROP_FPS) show=True # 初始化视频写入器 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) output_video = cv2.VideoWriter('output_idle_track.mp4', cv2.VideoWriter_fourcc(*'mp4v'), frame_rate, (frame_width, frame_height)) # 初始化输出目录 output_directory = "idle_snapshots" os.makedirs(output_directory, exist_ok=True) start_time = time.time() # Use time.time() to count the time stop_duration = 0 is_movement_detected = False is_object_detected = False is_stopped = False # 初始化静止标记 prev_x, prev_y = 0, 0 position_changes = [] threshold_frame_count = 10 threshold_factor = 1.5 while True: ret, frame = cap.read() if not ret: break # Perform object detection on the current frame results = model(frame)[0] # 修正:取列表中的第一个Results对象 # Get the bounding boxes of detected objects bboxes = results.xyxy[0].cpu().numpy()[:, :4] if len(results.xyxy) else [] if len(bboxes) == 0: # No object detected if is_object_detected: cv2.putText(frame, "No Object", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) print("No Object") else: cv2.putText(frame, "No Object", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) print("No Object") cv2.imshow('Tracking', frame) output_video.write(frame) continue is_object_detected = True # 只处理第一个检测到的目标 bbox = bboxes[0] (x_min, y_min, x_max, y_max) = bbox.astype(int) cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2) # Count the big position difference of the object position_change = ((x_min - prev_x)**2 + (y_min - prev_y)**2)**0.5 # Reset the time if there's big difference of position (object is moving) if position_change > 10: # Set the limit of position change that are considered big if is_stopped: is_stopped = False start_time = time.time() # Reset start time print('bergerak') # Set is_movement_detected become True when object is moving is_movement_detected = True if stop_duration >= 1.00 and is_movement_detected: # Create a name with timestamp timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f") output_filename = os.path.join(output_directory, f'frame_{timestamp}.jpg') text_duration = f'Stop Duration: {stop_duration:.2f}s' text_pos_x = f'x: {x_min:.2f}' text_prev_x = f'prev_x: {prev_x:.2f}' text_pos_y = f'y: {y_min:.2f}' text_prev_y = f'prev_y: {prev_y:.2f}' (text_width, text_height), baseline = cv2.getTextSize( text_duration, cv2.FONT_HERSHEY_SIMPLEX, 1, 2) # Count the position so the text is in the middle of the image text_x = (frame.shape[1] - text_width) // 2 text_y = text_height + 10 # Adjust to the desired position cv2.putText(frame, text_duration, (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(frame, text_pos_x, (text_x, text_y + 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(frame, text_prev_x, (text_x, text_y + 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(frame, text_pos_y, (text_x, text_y + 90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(frame, text_prev_y, (text_x, text_y + 120), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) # Save frame when the object is started moving again cv2.imwrite(output_filename, frame) print( f"The frame when the object moves again is saved as {output_filename}" ) # Reset is_movement_detected and stop_duration is_movement_detected = False stop_duration = 0 else: if not is_stopped: is_stopped = True start_time = time.time() # Start counting stop duration # Count stop duration stop_duration = time.time() - start_time print('stop') # Show stop duration on the top of bounding boxes text_duration = f'Stop Duration: {stop_duration:.2f}s' text_pos_x = f'x: {x_min:.2f}' text_prev_x = f'prev_x: {prev_x:.2f}' text_pos_y = f'y: {y_min:.2f}' text_prev_y = f'prev_y: {prev_y:.2f}' (text_width, text_height), baseline = cv2.getTextSize(text_duration, cv2.FONT_HERSHEY_SIMPLEX, 1, 2) # Count the position so the text is in the middle of the image text_x = (frame.shape[1] - text_width) // 2 text_y = text_height + 10 # Adjust to the desired position cv2.putText(frame, text_duration, (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(frame, text_pos_x, (text_x, text_y + 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(frame, text_prev_x, (text_x, text_y + 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(frame, text_pos_y, (text_x, text_y + 90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(frame, text_prev_y, (text_x, text_y + 120), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) # Update previous position prev_x, prev_y = x_min, y_min # Show frame with bounding boxes cv2.imshow('Tracking', frame) # Write the frame to the video output_video.write(frame) # Stop if 'q' is pressed key = cv2.waitKey(1) & 0xFF if key == ord("q") or key == ord("Q"): break cap.release() output_video.release() cv2.destroyAllWindows()
这样修改后,你的代码应该能正常运行,实现老鼠静止时长的统计啦!
备注:内容来源于stack exchange,提问作者Kaizennewbie




