You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

YOLOv8实现物体静止时长统计时出现AttributeError错误求助

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

看了你的代码,还有几个地方会导致后续报错,提前帮你指出来:

  1. is_stopped变量未初始化
    你代码里用到了is_stopped但开头没定义,会触发NameError,要在变量初始化部分加上:

    is_stopped = False  # 初始化静止状态标记
    
  2. 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))
    
  3. output_directory未定义
    你用到了output_directory但没创建/初始化,要在开头加上:

    output_directory = "idle_snapshots"
    os.makedirs(output_directory, exist_ok=True)  # 自动创建目录,不存在则新建
    
  4. 多目标跟踪的潜在问题
    如果视频里有多个老鼠,你的代码会把最后一个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

火山引擎 最新活动