YOLO目标追踪代码示例:「进出双向人流统计」和「越界报警」
本文博主再给你一个可直接落地的增强版脚本在原有「区域人流统计」基础上新增✅进出双向计数进1出-1✅越界报警非法闯入/离开触发✅轨迹方向判断基于中心点穿越区域边界✅报警弹窗 日志保存实现思路是用 TrackZone 做区域过滤 自己维护每个ID的历史中心点 → 判断穿越方向。一、实现原理先看这个代码更好懂TrackZone负责只在指定多边形区域内追踪目标返回track_idsboxes方向判断逻辑记录每个track_id上一帧的中心点(x1, y1)当前帧中心点(x2, y2)判断线段是否穿过区域某条边根据穿越边的法向量判断是「进入」还是「离开」越界报警进入/离开时触发画面红色高亮 控制台打印 日志文件二、完整代码进出双向统计 越界报警✅ 依赖ultralytics,opencv-python,numpyfromultralyticsimportYOLO,solutionsimportcv2importnumpyasnpfromdatetimeimportdatetime# # 1. 基础配置# MODEL_PATHyolo26n.ptVIDEO_PATHvideo.mp4# 0摄像头OUTPUT_PATHoutput_people_counting.mp4# 监控区域多边形顺时针region_pointsnp.array([(200,200),(1000,200),(1000,600),(200,600)],dtypenp.int32)# # 2. 全局状态# id_history{}# track_id - 上一帧中心点enter_count0exit_count0ALARM_LOGalarm.log# # 3. 工具函数# defpoint_in_polygon(pt,polygon):判断点是否在多边形内returncv2.pointPolygonTest(polygon,pt,False)0defsegment_intersects_polygon(p1,p2,polygon): 判断线段p1-p2是否穿过polygon任意边 返回 True 边的法向量方向 foriinrange(len(polygon)):atuple(polygon[i])btuple(polygon[(i1)%len(polygon)])# 线段ab边与线段p1p2相交ifcv2.clipLine(a,b,p1,p2)[0]:edge_vecnp.array(b)-np.array(a)normalnp.array([-edge_vec[1],edge_vec[0]])# 法向量returnTrue,normalreturnFalse,Nonedeflog_alarm(event,track_id):timestampdatetime.now().strftime(%Y-%m-%d %H:%M:%S)linef[{timestamp}] ALARM:{event}| TrackID{track_id}\nprint(line.strip())withopen(ALARM_LOG,a,encodingutf-8)asf:f.write(line)# # 4. 初始化 TrackZone# trackzonesolutions.TrackZone(modelMODEL_PATH,regionregion_points.tolist(),classes[0],# 只统计人trackerbytetrack.yaml,showFalse,persistTrue)# # 5. 视频IO# capcv2.VideoCapture(VIDEO_PATH)fpsint(cap.get(cv2.CAP_PROP_FPS))wint(cap.get(cv2.CAP_PROP_FRAME_WIDTH))hint(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))outcv2.VideoWriter(OUTPUT_PATH,cv2.VideoWriter_fourcc(*mp4v),fps,(w,h))# # 6. 主循环# whilecap.isOpened():success,framecap.read()ifnotsuccess:breakresultstrackzone(frame)# TrackZone返回的boxes和idsboxesresults.boxes.xyxy.cpu().numpy()idsresults.boxes.id.cpu().numpy().astype(int)forbox,tidinzip(boxes,ids):x1,y1,x2,y2map(int,box)cx,cy(x1x2)//2,(y1y2)//2pt(cx,cy)# 首次出现iftidnotinid_history:id_history[tid]ptcontinueprev_ptid_history[tid]id_history[tid]pt# 判断是否穿越区域边界intersect,normalsegment_intersects_polygon(prev_pt,pt,region_points)ifintersectandnormalisnotNone:move_vecnp.array(pt)-np.array(prev_pt)directionnp.dot(move_vec,normal)ifdirection0:enter_count1eventENTERelse:exit_count-1eventEXITlog_alarm(event,tid)# 越界报警红框高亮cv2.rectangle(frame,(x1,y1),(x2,y2),(0,0,255),3)cv2.putText(frame,fALARM:{event},(x1,y1-10),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,0,255),2)else:# 正常追踪框cv2.rectangle(frame,(x1,y1),(x2,y2),(0,255,0),2)cv2.putText(frame,fID:{tid},(x1,y1-10),cv2.FONT_HERSHEY_SIMPLEX,0.6,(0,255,0),2)# 绘制区域cv2.polylines(frame,[region_points],True,(255,0,0),2)# 统计信息cv2.putText(frame,fIn:{enter_count}Out:{exit_count}Total:{enter_countexit_count},(20,40),cv2.FONT_HERSHEY_SIMPLEX,1.2,(0,255,255),2)out.write(frame)cv2.imshow(People Counting Intrusion Alarm,frame)ifcv2.waitKey(1)0xFFord(q):break# # 7. 清理# cap.release()out.release()cv2.destroyAllWindows()print(✅ 处理完成)print(f进入人数:{enter_count})print(f离开人数:{abs(exit_count)})print(f当前在场人数:{enter_countexit_count})print(f报警日志:{ALARM_LOG})三、运行效果说明功能表现进入区域绿色框 In 1离开区域绿色框 Out -1越界瞬间红色高亮框 ALARM提示日志alarm.log自动记录输出视频带统计面板 报警标记四、常见优化建议场景优化斜向进出误判改用射线法 方向角度阈值多人密集提高track_buffer降低match_thresh夜间误报加亮度判断 / 红外摄像头只想统计“进入”删除 EXIT 分支即可