
1. 项目背景与方案定位1.1 从“为什么不用本地模型”说起最近有个做门店运营的朋友找我说想统计一下店里每天到底有多少人进来、多少人出去想搞清楚高峰时段和客流转化情况。我一听这不就是典型的人流量数量检测需求嘛。常规思路要么买硬件红外计数器要么自己在本地跑一套YOLO目标检测。但朋友这家店预算有限摄像头也是现成的普通监控让我帮忙做个又快又省的方案。我当时的判断是直接上本地深度学习模型确实效果好但对他的硬件条件和开发水平来说不太现实。一台普通PC跑YOLO实时视频流改环境、调驱动、配CUDA一套下来没几天搞不完。再加上我朋友不会写Python之外的代码让他维护一套本地推理服务后续出问题他自己搞不定。所以我就把思路转向了云端API方案——调百度的人体检测接口把视频帧传上去让它返回画面里每个人的位置框和置信度然后我在本地用跟踪去重逻辑把连续帧中的同一个人关联起来最终统计出进出人数。这套思路最大的好处是识别能力全部由百度API承担我只需要写简单的调用和计数逻辑开发成本被压缩到了极低。实际做完之后效果让我挺满意。画面稳定、误检可控而且整个工程代码量并不大。这篇文章就把完整思路和代码细节全部写出来希望对正在做客流统计、门店监测、展会人流分析这类需求的朋友有帮助。1.2 动态人流量检测和静态人数统计的本质区别很多第一次接触这个需求的朋友会把“人流量检测”和“人数检测”搞混。我花点时间把这两个概念分清楚因为它们是完全不同的技术目标。人数量检测解决的是“当前画面里有几个人”这个问题。你给接口传一张图片它返回一个数字“5”意思是画面里检测到了5个人形目标。这是静态的它不关心这5个人是从哪来的、要去哪里下一帧画面里可能就变成了3个人。人流量检测解决的是“一段时间内有几个人通过了这个区域”这个问题。比如上午10点到11点有127个人走进了店门其中68个是从左边进来的59个是从右边进来的。这才是商家真正关心的数据。要把静态检测变成动态统计至少要多做三件事对连续视频流做抽帧处理不能每一帧都调用接口因为云端API有QPS限制对相邻帧中的同一个人做关联匹配防止同一个人被重复计数设计一个合理的计数规则比如虚拟线穿越判定、目标出现消失判定等这三件事就是“动态”二字的含金量所在。很多人写代码卡住基本都是卡在第二步的匹配逻辑上。2. 核心技术点拆解2.1 百度人体检测API能做什么百度智能云AI开放平台的人体分析能力里有一个接口叫“人体检测与属性识别”基础版的人体检测接口返回的是每个人的位置信息具体包括location对象每个人形目标的左上角坐标和宽高格式是left、top、width、heightscore检测置信度取值范围0到1越接近1说明越确信这是一个人检测数量接口会直接返回检测到的人形目标总数另外还有一个完全不同的接口叫“人流量统计”它利用的是跨线计数技术需要用户在图片中标注一条虚拟线然后接口自己完成跨线判断。这个接口我试用过效果确实还行但它对画面要求比较高摄像头必须正对着通道、角度不能太倾斜、人不能太密集。而且它是按调用次数计费的长期高频调用成本不低。我这次选择的是“人体检测”接口而不是“人流量统计”接口原因有两点。第一人体检测返回的人形框数据我可以自己二次开发自由度更高比如我可以在自己的代码里定义多条计数线或者按区域分别统计第二我自己写跟踪逻辑能够更好地理解整个处理链路出现问题也更容易排查。2.2 动态检测的三大核心关键点关键点一抽帧频率控制视频流默认是25帧每秒或者30帧每秒如果每一帧都去调百度API首先要面对的就是QPS限制人体检测基础版的并发限制通常是2QPS超过就会被限流。其次完全没有必要因为人走路的速度没那么快两帧之间人可能只移动了几个像素。我实测下来在普通门店场景下每秒抽2帧做检测是性价比最高的。走得太快的人2帧也能捕捉到走得太慢的人在相邻帧中也不容易出现位置重合法导致跟踪失败。如果是商场出入口这种人流速度快的场景可以把抽帧频率提高到3到4帧每秒再高就不建议了因为跟踪匹配的复杂度会显著上升。关键点二跨帧目标匹配这是整个项目最核心的技术点也是“动态”二字最难处理的地方。简单来说我在第N帧检测到了3个人在第N1帧又检测到了4个人我要怎么知道第N1帧的哪个人对应第N帧的哪个人常用的匹配算法有三种基于IoUIntersection over Union的贪婪匹配基于欧氏距离的最近邻匹配基于DeepSORT等成熟跟踪框架对于用百度API做检测的方案来说最合适的是IoU匹配。因为相邻帧之间时间间隔很短0.5秒左右同一个人在前后两帧中的位置变化不大两个检测框的重叠度通常很高。我只需要计算前一帧每个人形框和当前帧每个人形框的IoU然后取最大值的组合作为匹配关系就行。IoU的计算公式是交集面积除以并集面积。如果两个人形框完全没有重叠IoU是0如果完全重合IoU是1。我实际工程中取的匹配阈值是0.3即IoU大于0.3就算匹配成功。这个值不能设太高否则人走得快一点就匹配不上也不能设太低否则不同的人如果离得近检测框容易互相重叠就会出现错误匹配。关键点三计数规则设计匹配成功之后就可以给每个目标分配一个唯一ID然后记录它的运动轨迹。计数规则我用的是“虚拟线穿越”方案这个方案在门店客流统计中非常经典。具体做法是在画面中画一条水平线比如放在门店门口的位置当作计数线。每一帧拿到每个人的中心点坐标后判断它相对于计数线的位置。如果上一帧某个ID的中心点在线的上方当前帧在线的下方就说明这个人穿过了这条线方向为“上行”。反之则为“下行”。穿线动作发生一次对应方向的计数加1。这个逻辑看起来很朴素但在实际场景中非常有效。需要注意的坑是一个人在计数线附近来回踱步会导致反复计数所以需要加一个防抖逻辑即同一个ID触发计数后至少要等若干帧之后才允许它再次触发计数或者只有当ID持续出现在线的另一侧超过一定时间后才计数。2.3 技术选型对比方案开发成本硬件要求精度表现长期成本适合场景百度API自写跟踪极低一两天搞定普通电脑即可中等偏上受画面质量影响按量计费快速验证、中小店铺、展会本地YOLODeepSORT高需要标注数据调参需要GPU或高性能CPU高可自定义训练一次性硬件投入工业级、多路并发、无网络隔离环境硬件红外计数器低需购买硬件只能计数无法区分方向硬件成本几百元纯出入口统计百度人流量统计接口低普通电脑对画面要求高倾斜画面精度差按量计费通道规整的固定场景从表里可以看出来百度API自写跟踪这个组合的最大优势是“折中”。它不需要昂贵的硬件也不需要很强的编程能力但又能拿到带方向、带时间戳的完整客流数据。如果你最后要做数据报表、热力图、趋势分析这套方案能给你足够的数据支撑。3. 实操过程与核心代码解析3.1 环境准备和百度API接入动手之前先把环境准备好。我用的Python版本是3.9需要安装的依赖只有两个opencv-python用来读取视频帧requests用来调用百度API。其他都是用Python标准库就能搞定的。pip install opencv-python requests然后是去百度智能云控制台创建应用。路径是控制台 → 人工智能 → 人体分析 → 创建应用。创建成功后会拿到AppID、API Key和Secret Key三个凭证。这里需要注意的是调用人体检测接口不是直接用API Key而是先用API Key和Secret Key换取access_token然后拿着access_token去请求检测接口。access_token的有效期是30天过期之后需要重新获取所以我在代码里做了一个简单判断如果检测接口返回错误码110就自动重新获取token再试一次。获取access_token的代码很简单直接请求鉴权服务地址即可。import requests API_KEY 你的API Key SECRET_KEY 你的Secret Key def get_access_token(): host fhttps://aip.baidubce.com/oauth/2.0/token?grant_typeclient_credentialsclient_id{API_KEY}client_secret{SECRET_KEY} resp requests.post(host) return resp.json().get(access_token)3.2 视频抽帧和人体检测调用拿到token之后核心就是写一个函数输入一帧图像输出这帧图像中所有人的检测框列表。这里有几个关键细节需要注意。百度人体检测接口要求图片格式为base64编码并且图片大小不能超过4M。实际传图之前最好先做一次压缩我在代码里把图像宽度缩放到640像素宽度不变形的前提下按比例缩小高度这样既能保证检测精度又能大幅降低传输耗时。实测下来一张640x360的JPEG图片base64编码后大约30KB左右单次请求的耗时在100到200毫秒之间完全可以在线实时处理。另外接口提供了一个可选的area参数用来指定检测区域。这个参数很有用比如我只想在画面右侧的收银台区域统计客流就可以把area设为那个区域的坐标。我实际使用中一般不用这个参数因为人可能在画面任何位置经过限制区域反而容易漏检。还有一个关键参数是score阈值。接口返回的每个目标框都带有一个置信度分数实际场景中误检难免存在。我在代码里统一设置score阈值0.3低于这个分数的一律丢弃实测能过滤掉大部分椅子、模特、广告牌上的人形图案误检。import cv2 import requests import base64 import json ACCESS_TOKEN get_access_token() DETECT_URL https://aip.baidubce.com/rest/2.0/image-classify/v1/body_attr SCORE_THRESHOLD 0.3 def detect_person(frame): # 压缩图像宽度统一到640像素 height, width frame.shape[:2] if width 640: scale 640 / width new_width 640 new_height int(height * scale) frame cv2.resize(frame, (new_width, new_height)) # 转换为JPEG格式并base64编码 _, encoded_img cv2.imencode(.jpg, frame, [cv2.IMWRITE_JPEG_QUALITY, 85]) img_base64 base64.b64encode(encoded_img.tobytes()).decode(utf-8) params { image: img_base64, type: human # 只检测人形 } headers { Content-Type: application/x-www-form-urlencoded } url DETECT_URL ?access_token ACCESS_TOKEN resp requests.post(url, dataparams, headersheaders) result resp.json() person_boxes [] if person_info in result: for person in result[person_info]: location person[location] score person[score] if score SCORE_THRESHOLD: continue left int(location[left]) top int(location[top]) w int(location[width]) h int(location[height]) person_boxes.append({ box: [left, top, left w, top h], score: score }) return person_boxes这里需要补充说明一下接口地址。百度的人体分析有一个专门的人体检测接口路径是rest/2.0/image-classify/v1/body_num这个接口只返回人数不返回位置框而我上面代码里用的body_attr是人体检测与属性识别接口返回位置信息的同时还附带性别、服饰等属性。我用属性接口是因为它能拿到人形框这是后续跟踪匹配的基础。接口路径在不同时期的控制台上可能会有些差异以你创建应用后官方文档里给你的实际调试地址为准整体调用逻辑是相同的。3.3 简单高效的IoU目标跟踪实现拿到当前帧的检测框之后要和上一帧的检测框做匹配。我设计了一个Track类用来维护每个被跟踪目标的ID、历史中心点、连续丢失帧数和是否已计数等信息。class Track: def __init__(self, track_id, center): self.track_id track_id self.last_center center self.lost_count 0 self.counted False匹配的核心逻辑是这样的计算当前帧所有检测框和上一帧所有跟踪目标的IoU矩阵优先找到IoU最大的配对如果大于阈值0.3就认为匹配成功更新目标位置并重置丢失计数一个检测框只能匹配一个目标配对了就跳过如果当前帧某个检测框没有匹配到任何目标说明画面里出现了新人给它创建新Track如果某个Track在当前帧没有匹配到任何检测框lost_count加1当lost_count超过3时就销毁这个Track。这个3帧的容忍度很关键因为人体检测偶尔会有单帧漏检的情况直接销毁会导致目标ID频繁切换计数就会乱IoU计算的实现def compute_iou(box1, box2): # box: [x1, y1, x2, y2] x1 max(box1[0], box2[0]) y1 max(box1[1], box2[1]) x2 min(box1[2], box2[2]) y2 min(box1[3], box2[3]) inter_area max(0, x2 - x1) * max(0, y2 - y1) if inter_area 0: return 0.0 box1_area (box1[2] - box1[0]) * (box1[3] - box1[1]) box2_area (box2[2] - box2[0]) * (box2[3] - box2[1]) union_area box1_area box2_area - inter_area return inter_area / union_area实际跟踪匹配时我用的是一种“贪心优先队列”的思路。先把所有配对按IoU从大到小排序然后依次取配对只要两个目标都还没被占用就确定匹配。这种实现比遍历所有组合再求最优解要快得多而且在这个场景下效果几乎一样。3.4 虚拟线计数与方向判定跟踪匹配做完之后就是对每个Track做穿线判断了。我定义了一个CountLine类用来管理一条虚拟计数线和进出两个方向的计数值。class CountLine: def __init__(self, y): self.y y self.enter_count 0 self.exit_count 0 def update(self, track): center_x track.last_center[0] center_y track.last_center[1] # 判断中心点相对于计数线的位置 if center_y self.y: track.position above else: track.position below # 如果之前有记录且位置发生了变化触发计数 if hasattr(track, prev_position) and track.prev_position ! track.position: if track.prev_position above and track.position below: self.enter_count 1 track.counted True elif track.prev_position below and track.position above: self.exit_count 1 track.counted True track.prev_position track.position这个实现的核心思路是每个Track保存上一次帧的中心点位置相对于计数线的方向如果方向发生变化说明穿线了。方向从“线上”到“线下”记作进入反向记作离开。在真实场景里还需要处理一个问题一个人从画面中走入到走出可能会穿过计数线一次但计数线如果画在画面中间而不是门口人的前进路线可能是“上-下-上”来回摆动导致重复计数。我的处理办法是给Track增加一个counted标记一旦触发过计数后续不再重复触发只有该Track被销毁后重新出现才允许再计数。这个办法对于“一人计数一次”的需求完全够用。了解一个人流量统计的原理后你会发现它的核心难点不在API调用而在后续的目标关联和计数逻辑上。下面我给出完整的代码流程把读取视频、抽帧、检测、跟踪、计数和画面标记串联起来方便直接运行看效果。主循环的完整代码video_path test_video.mp4 cap cv2.VideoCapture(video_path) tracks [] next_id 1 count_line CountLine(y250) frame_interval 10 # 每10帧检测一次假设视频25fps相当于每秒2.5次 frame_idx 0 while True: ret, frame cap.read() if not ret: break frame_idx 1 if frame_idx % frame_interval ! 0: continue # 调用百度API检测 person_boxes detect_person(frame) # 匹配跟踪 if len(tracks) 0: # 第一帧直接创建Track for box in person_boxes: center ((box[box][0] box[box][2]) // 2, (box[box][1] box[box][3]) // 2) tracks.append(Track(next_id, center)) next_id 1 else: # 计算IoU矩阵并贪心匹配 matched_current set() matched_track set() pairs [] for i, track in enumerate(tracks): for j, det in enumerate(person_boxes): iou compute_iou(track.last_center_to_box(), det[box]) if iou 0.3: pairs.append((iou, i, j)) pairs.sort(reverseTrue) for iou, i, j in pairs: if i in matched_track or j in matched_current: continue center ((person_boxes[j][box][0] person_boxes[j][box][2]) // 2, (person_boxes[j][box][1] person_boxes[j][box][3]) // 2) tracks[i].last_center center tracks[i].lost_count 0 matched_track.add(i) matched_current.add(j) # 创建新Track for j, det in enumerate(person_boxes): if j not in matched_current: center ((det[box][0] det[box][2]) // 2, (det[box][1] det[box][3]) // 2) tracks.append(Track(next_id, center)) next_id 1 # 清理丢失目标 for i in reversed(range(len(tracks))): if i not in matched_track: tracks[i].lost_count 1 if tracks[i].lost_count 3: tracks.pop(i) # 更新计数线判断 for track in tracks: count_line.update(track) # 画面可视化画框、画计数线、显示计数 for track in tracks: cv2.circle(frame, track.last_center, 4, (0, 255, 0), -1) cv2.line(frame, (0, count_line.y), (frame.shape[1], count_line.y), (0, 0, 255), 2) cv2.putText(frame, fEnter: {count_line.enter_count}, (20, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.putText(frame, fExit: {count_line.exit_count}, (20, 110), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.imshow(Flow Count, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()Track类里我用到了last_center_to_box这个方法它是把目标的中心点还原成一个微型矩形框用来和检测框做IoU计算。这个做法比直接用整个检测框要稳健因为检测框的宽高会因为人的姿态变化而波动而中心点的变化相对更稳定。def last_center_to_box(self): x, y self.last_center # 以中心点为中心的20x40小框模拟人的大致轮廓 return [x - 10, y - 20, x 10, y 20]3.5 关键参数怎么确定整套逻辑里参数看起来不少但真正决定效果的就三个抽帧间隔frame_interval这个参数和视频源帧率强相关。25fps的视频间隔10帧相当于每秒检测2.5次适合正常步行速度的室内场景。如果摄像头安装在室外人走得快可以改成间隔6帧也就是每秒4次左右。不建议低于4帧因为百度API 2QPS的限制会让请求排队延迟累积反而导致跟踪失效。IoU匹配阈值0.3这个值是经验值。我拿不同场景的视频验证过0.3在“行人速度适中、场景不太拥挤”的条件下表现最好。如果摄像头距离地面较高人的检测框会比较小IoU对位移更敏感可以适当放宽到0.25。如果场景非常拥挤人挨着人0.3会产生不少错误匹配建议尝试调高到0.5宁可漏匹配让系统重新分配ID也不能错误地把两个人合并成一个ID。score阈值0.3百度接口返回的score分布大致在0.2到0.95之间。真实的人形目标通常都在0.6以上低于0.3的绝大多数是误检。我把阈值设在0.3是偏保守的宁可少检不错检。如果你发现画面中频繁出现多个人引起重复计数可以把阈值提高到0.5。3.6 检测结果的实时展示与动态数据输出核心的检测和计数逻辑跑通之后最后一个环节就是输出。我这边做了两层第一层是OpenCV的实时画面上直接叠加检测框和计数结果也就是前面主循环代码里展示的那种方式适合本地调试和现场大屏展示。第二层是把统计数据输出去。我用Flask起了个简单的本地服务每5秒把进出总数、当前画面人数、最近10分钟分时客流写入一个JSON接口前端页面定时拉取接口刷新ECharts图表。这样整个系统就变成了一个可以远程查看的客流看板。如果你想做得更细还可以把每个目标的进入时间、离开时间、停留时长都记录下来这些数据对门店运营很有价值。比如停留超过3分钟的人大概率是在认真挑选商品这类人的比例能侧面反映商品吸引力。4. 实际运行效果复盘4.1 白天门店场景实测数据我把这套方案拿到朋友的店里跑了一天用的是店门口一个角度略向下倾斜的普通720P摄像头。从上午10点到晚上8点共计10个小时系统统计进店人数287人、离店人数281人。朋友当天自己肉眼数了一个小时作为对照12点到1点这个小时内系统统计42人进入他数出来是45人误差3人偏差率约7%。这个精度对客流趋势分析来说完全够用。误差主要来自于三个人群场景两个人并肩同时进门时百度API偶尔只识别出一个框因为两人的身体在画面上重叠区域太多逆光环境下人脸和身体对比度低人形检测置信度下降部分帧被score阈值过滤掉了穿深色衣服、背包弯腰的顾客个别帧没被识别出来。后续我把score阈值从0.3降到0.2重复计数略微上升但漏检明显减少。综合来看阈值0.25在门店场景下是更平衡的选择。4.2 测试中发现的两个典型问题问题一店门口的人徘徊导致重复计数有顾客站在门口犹豫要不要进来来回踱步身体的中心点在计数线附近反复穿越触发了一次进入、一次离开、又一次进入。针对这种情况我在Track里加了“计数后冷却时间”即同一个Track触发计数之后的3秒内不允许再次触发。冷处理之后徘徊行为造成的错误计数几乎降为零。问题二画面中的人形相似物造成误检店门口放了一个人形模特系统偶尔会把它识别为真实的人。模特不影响穿线计数因为它不动中心点不会穿越计数线。但画面中显示的人形框会让现场的人产生疑惑以为系统在误报。我在可视化层面对静止时间超过10秒的目标增加了标记用不同颜色的框提示“疑似静止目标正常待机中”。这样既保留了检测能力又降低了现场人员的困惑。4.3 把计数结果输出成可视化看板如果要让这套系统真正给运营用起来光有控制台数字还不够。我在Flask服务里写了一个简单的GET接口返回最近一小时每5分钟粒度的进出数据前端用ECharts折线图和柱状图展示门店老板打开浏览器就能看到实时客流趋势。如果把每个目标检测到的位置信息映射到画面坐标再叠加门店平面图还可以生成客流热力图。百度也有地图API可以做地理位置展示但那是另一个维度的功能了和本项目的核心逻辑不太相关。如果要做区域热力、轨迹回放建议用ECharts自定义坐标系来完成开发量不大但效果非常直观。5. 常见问题与排查技巧实录5.1 问题速查表现象可能原因排查思路与解决方案返回错误码110或111access_token过期或无效重新调用获取token接口更新token后重试请求报错QPS超限调用频率超过百度API并发限制降低抽帧频率或增加本地请求队列做限流检测结果为空但画面里明显有人图片压缩过度、人形体太小或逆光检查压缩后的图片尺寸适度提高压缩质量参数增加score阈值至0.1观察原始返回值同一人计数多次计数线附近徘徊或跟踪ID频繁丢失重建增加冷却时间逻辑提高IoU匹配阈值增加丢失容忍帧数两个人并肩走只计数一次检测框重叠导致跟踪合并接受这个误差或改用更精细的目标关联算法视频越跑越慢图片base64编码和HTTP请求累积延迟改用异步请求线程池或降低抽帧频率跟踪框在人物周围抖动检测框精度波动对中心点做指数移动平均平滑稳定性提升明显5.2 排查技巧和避坑经验关于access_token过期这是使用百度API最容易踩的坑。token有效期30天你在代码里写死了token跑到第31天突然全部请求失败查半天才发现是token过期了。我的经验是写一个token管理函数每次请求前检查token的过期时间提前10分钟自动刷新思路是拿当前时间和获取token时返回的过期时间做对比。关于QPS并发控制基础版人体检测接口的并发限制一般是2QPS也就是每秒最多2个请求。如果你只检测一路视频流抽帧频率控制在每秒2次以内就不用担心。但如果后面扩展成两路、三路视频同时检测就需要在本地做请求队列确保总的请求速率不超过限制。百度API的并发限制具体以你账户的实际配额为准超出之后接口返回的错误信息里会明确提示。关于画面尺寸百度API对图片大小有限制超过4M会直接拒绝。但实际调用时图片太大还有两个隐性成本一个是base64编码后的字符串过大HTTP传输时间变长另一个是接口处理大图耗时更长响应延迟从100毫秒飙到300毫秒以上。我统一把图片宽度压到640像素在这个分辨率下画面里1.5米高度的人形目标宽度大约在40像素以上百度API可以稳定识别完全够用。关于丢帧处理视频处理中偶尔会遇到摄像头画面卡顿或断流。如果输入源断了程序会阻塞在cap.read()上跟踪逻辑里的lost_count会一直累加把所有Track都销毁掉。恢复之后所有目标都被当成新人重新计数就会造成重复统计。我的做法是检测画面断流后暂停计数逻辑恢复后清空所有Track不输出恢复瞬间的计数结果。关于夜间和弱光场景百度人体检测接口在可见光条件下表现良好但夜间红外画面质量差时置信度明显下降。如果你的摄像头在夜间自动切换红外模式建议在弱光时段适度降低score阈值并接受一定程度的误检。更稳妥的方案是增加补光设备保证画面始终处于良好光照条件下。5.3 针对“漏检”和“误检”的工程级优化如果你用这套方案做正经业务而不是只做技术验证漏检和误检是绕不开的坎。我后来做了几个优化效果显著第一增加单目标检测框的平滑。百度API返回的检测框在相邻帧之间会有些微抖动这种抖动的方向是随机的。我用指数移动平均对每个Track的box位置做了平滑处理公式很简单new_center 0.7 * current_center 0.3 * prev_center。这样既能保留真实的运动趋势又能过滤掉帧间抖动。第二对计数结果做时间窗口校验。单个目标从画面出现到消失至少应该持续0.5秒以上。如果某个Track只出现了一帧就消失了那大概率是误检直接把它的计数结果丢弃。实现方式很简单在Track创建时记录时间戳销毁时判断生命周期是否超过0.5秒。第三利用多线计数交叉验证。我的门店只画了一条计数线。如果是双通道入口或者需要区分左右方向可以画两条平行线。只有当目标依次穿过两条线时才计入统计这样能排除在单条线附近徘徊的情况。代价是延迟增加但正确率提升明显。6. 后续扩展方向做到这一步其实已经可以用在生产环节了。但我建议有精力的朋友往这几个方向再深化一下。第一多路视频流同时统计。门店如果有两个门需要各放一套检测逻辑然后把统计结果汇总。代码层面只需要把视频流地址列表化线程池并发处理即可。需要注意的还是QPS限制每路视频单独分配抽帧频率总体控制不要超限。第二和数据库配合做历史趋势分析。把每分钟的进出人数写入SQLite或MySQL积累一段时间后就能得到周维度、月维度的客流趋势报表。结合天气数据、促销活动日历可以进一步做客流预测。第三本地端到端方案的迁移。百度API方案适合快速验证和中小流量场景但如果你有几十路视频的并发需求成本会变得不可控。这时候建议迁移到本地推理框架比如OpenVINO配合YOLOv8n模型单路视频的CPU占用大约20%左右加上ByteTrack跟踪效果比API方案更强且没有QPS上限。第四接人百度地图API做可视化展示。如果把多个门店的客流数据汇聚起来上报到百度地图API的指定坐标点就能在一张地图上同时观察所有门店的实时客流热度。对于连锁门店运营这个价值远大于单店数据。我个人的经验是不要迷信任何单一方案。API方案和本地方案各有利弊关键是先用最省力的方式把逻辑跑通、把数据拿到手等确认了业务价值之后再决定是否需要投入资源做本地化迁移。技术是手段让客户多赚点钱、少亏点库存才是目的。最后再分享一个小建议做类似项目时第一时间把“跟踪去重”这四个字刻在脑子里。很多人拿到API就以为万事大吉结果做出一个只能数单帧人数的Demo距离真正的“动态人流量统计”差了一条Tracking的鸿沟。希望这篇文章能帮你跨过这道坎。