prompt优化、ROI更新
This commit is contained in:
@@ -13,7 +13,7 @@ llm:
|
||||
common:
|
||||
# 工作时间段:支持多个时间段,格式为 [开始小时, 开始分钟, 结束小时, 结束分钟]
|
||||
# 8:30-11:00, 12:00-17:30
|
||||
working_hours:
|
||||
working_hours:
|
||||
- [8, 30, 11, 0] # 8:30-11:00
|
||||
- [12, 0, 17, 30] # 12:00-17:30
|
||||
process_every_n_frames: 3 # 每3帧处理1帧(用于人员离岗)
|
||||
|
||||
179
monitor.py
179
monitor.py
@@ -33,11 +33,19 @@ def save_alert_image(frame, cam_id, roi_name, alert_type, alert_info=""):
|
||||
|
||||
os.makedirs(alert_dir, exist_ok=True)
|
||||
|
||||
# 生成文件名:摄像头ID_ROI名称_时间戳.jpg
|
||||
# 生成文件名:根据告警类型使用不同的命名方式
|
||||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
# 清理文件名中的特殊字符
|
||||
safe_roi_name = roi_name.replace("/", "_").replace("\\", "_").replace(":", "_")
|
||||
filename = f"{cam_id}_{safe_roi_name}_{timestamp}.jpg"
|
||||
|
||||
# 对于入侵告警,使用告警类型+ROI名称;对于离岗告警,使用ROI名称
|
||||
if alert_type == "入侵":
|
||||
# 周界入侵:使用"入侵_区域名称"格式
|
||||
filename = f"{cam_id}_入侵_{safe_roi_name}_{timestamp}.jpg"
|
||||
else:
|
||||
# 离岗:使用原有格式
|
||||
filename = f"{cam_id}_{safe_roi_name}_{timestamp}.jpg"
|
||||
|
||||
filepath = os.path.join(alert_dir, filename)
|
||||
|
||||
# 保存图片
|
||||
@@ -139,12 +147,12 @@ class LLMClient:
|
||||
请根据以下规则生成结构化响应:
|
||||
|
||||
### 【判定标准】
|
||||
✅ **本单位物业员工**需同时满足:
|
||||
1. **清晰可见的正式工牌**(胸前佩戴,含照片/姓名/公司LOGO)
|
||||
2. **穿着标准制服**(如:黄蓝工程服、白衬衫+黑领带、蓝色清洁装、浅色客服装等)
|
||||
✅ **本单位物业员工**需满足下列条件之一:
|
||||
1. **清晰可见的正式工牌**(胸前佩戴)
|
||||
2. **穿着标准制服**(如:带有白色反光条的深色工程服、黄蓝工程服、白衬衫+黑领带、蓝色清洁装、浅色客服装等)
|
||||
3. **行为符合岗位规范**(如巡检、维修、清洁,无徘徊、张望、翻越)
|
||||
|
||||
> ⚠️ 仅满足部分条件(如仅有安全帽、仅有类似颜色衣服、工牌不可见)→ 视为非员工。
|
||||
> 注意: 满足部分关键条件(如戴有安全帽、穿有工作人员服饰、带有工牌)→ 视为员工,不生成告警。
|
||||
|
||||
### 【输出规则】
|
||||
#### 情况1:ROI区域内**无人**
|
||||
@@ -174,11 +182,11 @@ class LLMClient:
|
||||
|
||||
▶ 示例2(员工):
|
||||
🟢无异常:检测到本单位工作人员正常作业。
|
||||
1名工程人员穿黄蓝工服佩戴工牌在高配间巡检。
|
||||
1名工程人员穿带有反光条的深蓝色工服在高配间巡检。
|
||||
|
||||
▶ 示例3(非员工):
|
||||
🚨天台区域入侵告警:检测到疑似非工作人员,请立即核查。
|
||||
1人穿黑色外套未佩戴工牌进入天台区域并短暂停留。
|
||||
1人穿绿色外套未佩戴工牌进入天台区域。
|
||||
|
||||
---
|
||||
请分析摄像头{cam_id}的{roi_name}区域,按照上述格式输出结果。"""
|
||||
@@ -308,25 +316,53 @@ class ThreadedFrameReader:
|
||||
def __init__(self, cam_id, rtsp_url):
|
||||
self.cam_id = cam_id
|
||||
self.rtsp_url = rtsp_url
|
||||
self.cap = cv2.VideoCapture(rtsp_url, cv2.CAP_FFMPEG)
|
||||
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
|
||||
self._lock = threading.Lock() # 添加锁保护VideoCapture访问
|
||||
self.cap = None
|
||||
try:
|
||||
self.cap = cv2.VideoCapture(rtsp_url, cv2.CAP_FFMPEG)
|
||||
if self.cap.isOpened():
|
||||
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
|
||||
else:
|
||||
print(f"[{cam_id}] 警告:无法打开视频流: {rtsp_url}")
|
||||
except Exception as e:
|
||||
print(f"[{cam_id}] 初始化VideoCapture失败: {e}")
|
||||
self.q = queue.Queue(maxsize=2)
|
||||
self.running = True
|
||||
self.thread = threading.Thread(target=self._reader, daemon=True)
|
||||
self.thread.start()
|
||||
|
||||
def _reader(self):
|
||||
while self.running:
|
||||
ret, frame = self.cap.read()
|
||||
if not ret:
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
if self.q.full():
|
||||
try:
|
||||
self.q.get_nowait()
|
||||
except queue.Empty:
|
||||
pass
|
||||
self.q.put(frame)
|
||||
"""读取帧的线程函数"""
|
||||
try:
|
||||
while self.running:
|
||||
with self._lock:
|
||||
if self.cap is None or not self.cap.isOpened():
|
||||
break
|
||||
ret, frame = self.cap.read()
|
||||
|
||||
if not ret:
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
if self.q.full():
|
||||
try:
|
||||
self.q.get_nowait()
|
||||
except queue.Empty:
|
||||
pass
|
||||
self.q.put(frame)
|
||||
except Exception as e:
|
||||
print(f"[{self.cam_id}] 读取帧线程异常: {e}")
|
||||
finally:
|
||||
# 确保资源释放(使用锁保护)
|
||||
with self._lock:
|
||||
if self.cap is not None:
|
||||
try:
|
||||
if self.cap.isOpened():
|
||||
self.cap.release()
|
||||
except Exception as e:
|
||||
print(f"[{self.cam_id}] 释放VideoCapture时出错: {e}")
|
||||
finally:
|
||||
self.cap = None
|
||||
|
||||
def read(self):
|
||||
if not self.q.empty():
|
||||
@@ -334,8 +370,26 @@ class ThreadedFrameReader:
|
||||
return False, None
|
||||
|
||||
def release(self):
|
||||
"""释放资源,等待线程结束"""
|
||||
if not self.running:
|
||||
return # 已经释放过了
|
||||
|
||||
self.running = False
|
||||
self.cap.release()
|
||||
|
||||
# 等待线程结束,最多等待3秒
|
||||
if self.thread.is_alive():
|
||||
self.thread.join(timeout=3.0)
|
||||
if self.thread.is_alive():
|
||||
print(f"[{self.cam_id}] 警告:读取线程未能在3秒内结束")
|
||||
|
||||
# 清空队列
|
||||
while not self.q.empty():
|
||||
try:
|
||||
self.q.get_nowait()
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
# VideoCapture的释放由_reader线程的finally块处理,这里不再重复释放
|
||||
|
||||
|
||||
class MultiCameraMonitor:
|
||||
@@ -497,10 +551,39 @@ class MultiCameraMonitor:
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
"""停止监控,清理所有资源"""
|
||||
print("正在停止监控系统...")
|
||||
self.running = False
|
||||
for reader in self.frame_readers.values():
|
||||
reader.release()
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
# 等待推理线程结束
|
||||
if hasattr(self, 'inference_thread') and self.inference_thread.is_alive():
|
||||
self.inference_thread.join(timeout=2.0)
|
||||
|
||||
if hasattr(self, 'perimeter_thread') and self.perimeter_thread.is_alive():
|
||||
self.perimeter_thread.join(timeout=2.0)
|
||||
|
||||
# 释放所有摄像头资源
|
||||
for cam_id, reader in self.frame_readers.items():
|
||||
try:
|
||||
print(f"正在释放摄像头 {cam_id}...")
|
||||
reader.release()
|
||||
except Exception as e:
|
||||
print(f"释放摄像头 {cam_id} 时出错: {e}")
|
||||
|
||||
# 关闭所有窗口
|
||||
try:
|
||||
cv2.destroyAllWindows()
|
||||
except:
|
||||
pass
|
||||
|
||||
# 强制清理(如果还有线程在运行)
|
||||
import sys
|
||||
import os
|
||||
if sys.platform == 'win32':
|
||||
# Windows下可能需要额外等待
|
||||
time.sleep(0.5)
|
||||
|
||||
print("监控系统已停止")
|
||||
|
||||
|
||||
class ROILogic:
|
||||
@@ -558,7 +641,7 @@ class ROILogic:
|
||||
# 周界入侵相关状态(如果没有points,使用整张画面)
|
||||
if '周界入侵' in self.algorithms:
|
||||
self.perimeter_last_check_time = 0
|
||||
self.perimeter_alert_cooldown = 60 # 周界入侵告警冷却60秒
|
||||
self.perimeter_alert_cooldown = 120 # 周界入侵告警冷却120秒(2分钟)
|
||||
if self.use_full_frame:
|
||||
print(f"[{cam_id}] 提示:{self.roi_name} 周界入侵算法将使用整张画面进行检测")
|
||||
|
||||
@@ -903,13 +986,13 @@ class CameraLogic:
|
||||
# 非工作人员
|
||||
print(f"[{self.cam_id}] [{roi.roi_name}] 🚨 周界入侵告警!检测到非工作人员(检测区域:{area_desc})")
|
||||
print(f"大模型判断结果: {result}")
|
||||
# 保存告警图片
|
||||
# 保存告警图片(使用区域描述作为名称,更清晰)
|
||||
save_alert_image(
|
||||
frame.copy(),
|
||||
self.cam_id,
|
||||
roi.roi_name,
|
||||
area_desc, # 使用area_desc而不是roi.roi_name
|
||||
"入侵",
|
||||
f"检测区域: {area_desc}\n大模型判断结果:\n{result}"
|
||||
f"检测区域: {area_desc}\nROI名称: {roi.roi_name}\n大模型判断结果:\n{result}"
|
||||
)
|
||||
else:
|
||||
# 工作人员
|
||||
@@ -919,13 +1002,13 @@ class CameraLogic:
|
||||
# 没有大模型时,直接告警
|
||||
area_desc = "整张画面" if roi.use_full_frame else roi.roi_name
|
||||
print(f"[{self.cam_id}] [{roi.roi_name}] 🚨 周界入侵告警!检测到人员进入(检测区域:{area_desc})")
|
||||
# 保存告警图片
|
||||
# 保存告警图片(使用区域描述作为名称,更清晰)
|
||||
save_alert_image(
|
||||
frame.copy(),
|
||||
self.cam_id,
|
||||
roi.roi_name,
|
||||
area_desc, # 使用area_desc而不是roi.roi_name
|
||||
"入侵",
|
||||
f"检测区域: {area_desc}\n检测到人员进入"
|
||||
f"检测区域: {area_desc}\nROI名称: {roi.roi_name}\n检测到人员进入"
|
||||
)
|
||||
|
||||
self.display_frame = frame.copy()
|
||||
@@ -1014,12 +1097,40 @@ class CameraLogic:
|
||||
|
||||
|
||||
def main():
|
||||
import signal
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--config", default="config.yaml", help="配置文件路径")
|
||||
args = parser.parse_args()
|
||||
|
||||
monitor = MultiCameraMonitor(args.config)
|
||||
monitor.run()
|
||||
monitor = None
|
||||
try:
|
||||
monitor = MultiCameraMonitor(args.config)
|
||||
|
||||
# 注册信号处理,确保优雅退出
|
||||
def signal_handler(sig, frame):
|
||||
print("\n收到退出信号,正在关闭...")
|
||||
if monitor:
|
||||
monitor.stop()
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
if sys.platform != 'win32':
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
monitor.run()
|
||||
except KeyboardInterrupt:
|
||||
print("\n收到键盘中断,正在关闭...")
|
||||
except Exception as e:
|
||||
print(f"程序异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
if monitor:
|
||||
monitor.stop()
|
||||
# 确保进程退出
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user