You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python和OpenCV的人员计数系统跟踪问题求助

问题:楼宇人员计数系统人员跟踪失效

我用Python和OpenCV开发了一套楼宇人员计数系统,但目前人员跟踪功能存在严重问题:房间内实际有3人,系统却仅计数1人。我已经尝试调整maxDisappear、maxDistance以及confidence参数,但没能彻底解决问题。

相关代码

from mylib.centroidtracker import CentroidTracker
from mylib.trackableobject import TrackableObject
from imutils.video import VideoStream
from imutils.video import FPS
#from mylib.mailer import Mailer
from mylib import config, thread
import time, schedule, csv
import numpy as np
import argparse, imutils
import time, dlib, cv2, datetime
from itertools import zip_longest
#from pykalman import KalmanFilter

#python main.py --prototxt mobilenet_ssd/MobileNetSSD_deploy.prototxt --model mobilenet_ssd/MobileNetSSD_deploy.caffemodel --input videos/video.mp4
#kf = KalmanFilter(initial_state_mean=[0, 0], n_dim_obs=2)
t0 = time.time()

def is_above_boundary_line(x, y, slope, intercept):
    return y < (slope * x + intercept)

def run():
    ap = argparse.ArgumentParser()
    ap.add_argument("-p", "--prototxt", required=False,
        help="path to Caffe 'deploy' prototxt file")
    ap.add_argument("-m", "--model", required=True,
        help="path to Caffe pre-trained model")
    ap.add_argument("-i", "--input", type=str,
        help="path to optional input video file")
    ap.add_argument("-o", "--output", type=str,
        help="path to optional output video file")
    ap.add_argument("-c", "--confidence", type=float, default=1,
        help="minimum probability to filter weak detections")
    ap.add_argument("-s", "--skip-frames", type=int, default=4,
        help="# of skip frames between detections")
    args = vars(ap.parse_args())

    CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
        "bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
        "dog", "horse", "motorbike", "person", "pottedplant", "sheep",
        "sofa", "train", "tvmonitor"]

    net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])

    if not args.get("input", False):
        print("[INFO] Starting the live stream..")
        vs = VideoStream(config.url).start()
        time.sleep(2.0)
    else:
        print("[INFO] Starting the video..")
        vs = cv2.VideoCapture(args["input"])

    writer = None
    W = None
    H = None

    ct = CentroidTracker(maxDisappeared=300, maxDistance=200)
    trackers = []
    trackableObjects = {}

    totalFrames = 0
    totalDown = 0
    totalUp = 0
    x = []
    empty = []
    empty1 = []

    fps = FPS().start()

    if config.Thread:
        vs = thread.ThreadingClass(config.url)

    while True:
        frame = vs.read()
        frame = frame[1] if args.get("input", False) else frame
        if frame is None:
            break
        
        frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        frame = imutils.resize(frame, width=500)
        rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        if W is None or H is None:
            (H, W) = frame.shape[:2]
            x1, y1 = 0, int(H * 0.9)
            x2, y2 = int(W * 0.85), 0
            boundary_line_slope = (y2 - y1) / (x2 - x1)
            boundary_line_intercept = y1 - boundary_line_slope * x1

        if args["output"] is not None and writer is None:
            fourcc = cv2.VideoWriter_fourcc(*"MJPG")
            writer = cv2.VideoWriter(args["output"], fourcc, 30, (W, H), True)

        status = "Waiting"
        rects = []

        if totalFrames % args["skip_frames"] == 0:
            status = "Detecting"
            trackers = []

            blob = cv2.dnn.blobFromImage(frame, 0.007843, (W, H), 127.5)
            net.setInput(blob)
            detections = net.forward()

            for i in np.arange(0, detections.shape[2]):
                confidence = detections[0, 0, i, 2]

                if confidence > 0.2:
                    idx = int(detections[0, 0, i, 1])

                    if CLASSES[idx] != "person":
                        continue

                    box = detections[0, 0, i, 3:7] * np.array([W, H, W, H])
                    (startX, startY, endX, endY) = box.astype("int")

                    tracker = dlib.correlation_tracker()
                    rect = dlib.rectangle(startX, startY, endX, endY)
                    tracker.start_track(rgb, rect)

                    trackers.append(tracker)
        else:
            for tracker in trackers:
                status = "Tracking"
                tracker.update(rgb)
                pos = tracker.get_position()

                startX = int(pos.left())
                startY = int(pos.top())
                endX = int(pos.right())
                endY = int(pos.bottom())

                rects.append((startX, startY, endX, endY))

        cv2.line(frame, (0, int(H * 0.9)), (int(W * 0.85), 0), (255, 0, 0), 3)

        objects = ct.update(rects)

        for (objectID, centroid) in objects.items():
            to = trackableObjects.get(objectID, None)

            if to is None:
                to = TrackableObject(objectID, centroid)
            else:
               #y = [c[1] for c in to.centroids]
               #direction = centroid[1] - np.mean(y)
                to.centroids.append(centroid)

                if not to.counted:
                    if len(to.centroids) > 1:
                        prev_position = to.centroids[-2]
                        if is_above_boundary_line(prev_position[0], prev_position[1], boundary_line_slope, boundary_line_intercept) and not is_above_boundary_line(centroid[0], centroid[1], boundary_line_slope, boundary_line_intercept):
                            totalDown += 1
                            empty1.append(totalDown)
                            to.counted = True
                        elif not is_above_boundary_line(prev_position[0], prev_position[1], boundary_line_slope, boundary_line_intercept) and is_above_boundary_line(centroid[0], centroid[1], boundary_line_slope, boundary_line_intercept):
                            totalUp += 1
                            empty.append(totalUp)
                            to.counted = True

            trackableObjects[objectID] = to

            text = "ID {}".format(objectID)
            cv2.putText(frame, text, (centroid[0] - 10, centroid[1] - 10),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
            cv2.circle(frame, (centroid[0], centroid[1]), 4, (0, 255, 0), -1)

        info = [
            ("Exit", totalUp),
            ("Enter", totalDown),
           # ("Status", status),
        ]

        for (i, (k, v)) in enumerate(info):
            text = "{}: {}".format(k, v)
            cv2.putText(frame, text, (10, H - ((i * 20) + 20)),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)

        if writer is not None:
            writer.write(frame)
            
            
        frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        cv2.imshow("Frame", frame)
        key = cv2.waitKey(1) & 0xFF

        if key == ord("q"):
            break

        totalFrames += 1
        fps.update()

    fps.stop()
    print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
    print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))

    if writer is not None:
        writer.release()

    if not args.get("input", False):
        vs.stop()
    else:
        vs.release()

    cv2.destroyAllWindows()

    d = [datetime.datetime.now()]
    dts = [ts.strftime("%A %d %B %Y %I:%M:%S%p") for ts in d]
    export_data = zip_longest(*[dts, empty, empty1], fillvalue='')

    with open('Log.csv', 'w', newline='') as file:
        writer = csv.writer(file)
        writer.writerow(("End Time", "In", "Out"))
        writer.writerows(export_data)

run()
修复建议
  • 统一检测置信度逻辑:代码中命令行参数--confidence默认值设为1,但检测时却用confidence > 0.2判断,导致参数完全失效。将检测条件改为confidence > args["confidence"],并把默认值调整到0.4左右,避免漏检人员。
  • 优化跟踪频率:当前每4帧才重新检测一次,期间仅靠dlib跟踪器,人员快速移动或遮挡时容易丢失目标。降低skip-frames值到2,增加检测频率;或者在跟踪阶段加入校验,若跟踪框与上一帧偏差超过阈值,触发临时检测。
  • 改进质心跟踪匹配规则:如果CentroidTracker仅用欧氏距离匹配质心,人员密集时会出现ID混淆。可以结合检测框的IOU(交并比)进行匹配,同时根据场景调整maxDistance——画面中人员间距小时,该值不宜过大,否则会把不同人员匹配为同一ID。
  • 调整计数触发条件:当前需要积累至少2个质心才判断越线,人员快速通过时可能错过计数。可以增加首次检测时的位置判断,或者放宽计数触发的条件。
  • 简化颜色空间转换:代码中多次转换颜色空间(RGB2BGR→BGR2RGB),可能导致画面异常影响检测。简化转换步骤,确保输入到检测网络和跟踪器的画面格式正确。

内容的提问来源于stack exchange,提问作者Zer_W

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 12:45:55