feat: 本地视觉分析模块 - 运动检测、人体检测、亮度检测,自动判断是否需要大模型

This commit is contained in:
2026-04-16 14:11:53 +08:00
parent 49b6f7aafe
commit cbd0b2f86c
2 changed files with 452 additions and 17 deletions
+103 -17
View File
@@ -6,6 +6,7 @@ import time
import datetime
from camera import CameraCapture
from analyzer import ImageAnalyzer
from local_analyzer import LocalAnalyzer
from database import db
from config import config_mgr
@@ -15,15 +16,19 @@ class VisionScheduler:
def __init__(self):
self.camera = CameraCapture()
self.analyzer = ImageAnalyzer()
self.vision_analyzer = ImageAnalyzer() # 大模型分析器
self.local_analyzer = LocalAnalyzer() # 本地分析器
self.running = False
self.timer = None
self.prev_image_path = None # 保存前一张图片路径
# 统计
self.capture_count = 0
self.last_capture_time = None
self.last_analyze_time = None
self.errors = []
self.model_calls = 0 # 大模型调用次数
self.local_analyses = 0 # 本地分析次数
def start(self):
"""启动定时拍照"""
@@ -94,33 +99,88 @@ class VisionScheduler:
self._schedule_next()
def _analyze_task(self, image_id, image_path):
"""分析任务"""
"""分析任务 - 先本地分析,再决定是否调用大模型"""
try:
result = self.analyzer.analyze(image_path)
self.local_analyses += 1
if result['success']:
# 记录事件
for event in result['events']:
# 1. 本地快速分析
local_result = self.local_analyzer.analyze(image_path, self.prev_image_path)
# 保存当前图片路径供下次对比
self.prev_image_path = image_path
if local_result['success']:
# 记录本地检测到的事件
for event in local_result['events']:
db.add_event(
image_id,
event['event_type'],
event['event_type'] + '(本地)',
event['description'],
event['confidence']
)
# 标记已分析
db.mark_image_analyzed(image_id)
# 2. 判断是否需要大模型分析
if local_result['need_model'] and config_mgr.get('auto_analyze', True):
print(f"[Scheduler] Local analysis triggered model call for image {image_id}")
self._call_vision_api(image_id, image_path)
else:
# 不需要大模型,直接标记已分析
db.mark_image_analyzed(image_id)
print(f"[Scheduler] Local analysis sufficient for image {image_id}")
print(f" - Motion: {local_result['metrics'].get('motion_ratio', 0):.2%}")
print(f" - Human: {local_result['metrics'].get('human_count', 0)}")
print(f" - Need model: {local_result['need_model']}")
self.last_analyze_time = datetime.datetime.now().isoformat()
else:
self.errors.append({
'time': datetime.datetime.now().isoformat(),
'error': f"分析失败: {result['error']}"
'error': f"本地分析失败: {local_result['error']}"
})
# 本地分析失败,尝试直接调用大模型
if config_mgr.get('auto_analyze', True):
self._call_vision_api(image_id, image_path)
except Exception as e:
self.errors.append({
'time': datetime.datetime.now().isoformat(),
'error': str(e)
})
def _call_vision_api(self, image_id, image_path):
"""调用大模型 Vision API"""
try:
self.model_calls += 1
print(f"[Scheduler] Calling Vision API for image {image_id}")
result = self.vision_analyzer.analyze(image_path)
if result['success']:
for event in result['events']:
db.add_event(
image_id,
event['event_type'] + '(AI)',
event['description'],
event['confidence']
)
db.mark_image_analyzed(image_id)
print(f"[Scheduler] Vision API analysis complete for image {image_id}")
else:
print(f"[Scheduler] Vision API failed: {result['error']}")
self.errors.append({
'time': datetime.datetime.now().isoformat(),
'error': f"Vision API失败: {result['error']}"
})
# 即使失败也标记已分析(避免重复调用)
db.mark_image_analyzed(image_id)
except Exception as e:
print(f"[Scheduler] Vision API exception: {e}")
self.errors.append({
'time': datetime.datetime.now().isoformat(),
'error': str(e)
})
def capture_now(self):
"""立即拍照"""
result = self.camera.capture()
@@ -157,20 +217,43 @@ class VisionScheduler:
if not image:
return {'success': False, 'error': '图片不存在'}
result = self.analyzer.analyze(image['path'])
# 获取前一张图片
prev_images = db.get_images(limit=1, offset=1)
prev_path = prev_images[0]['path'] if prev_images else None
if result['success']:
for event in result['events']:
# 先本地分析
local_result = self.local_analyzer.analyze(image['path'], prev_path)
if local_result['success']:
# 记录本地事件
for event in local_result['events']:
db.add_event(
image_id,
event['event_type'],
event['event_type'] + '(本地)',
event['description'],
event['confidence']
)
db.mark_image_analyzed(image_id)
self.last_analyze_time = datetime.datetime.now().isoformat()
# 再调用大模型(强制调用,用户手动点击)
vision_result = self.vision_analyzer.analyze(image['path'])
if vision_result['success']:
for event in vision_result['events']:
db.add_event(
image_id,
event['event_type'] + '(AI)',
event['description'],
event['confidence']
)
db.mark_image_analyzed(image_id)
self.last_analyze_time = datetime.datetime.now().isoformat()
return {'success': True, 'events': local_result['events'] + vision_result['events']}
else:
db.mark_image_analyzed(image_id)
return {'success': True, 'events': local_result['events'], 'vision_error': vision_result['error']}
return local_result
return result
except Exception as e:
return {'success': False, 'error': str(e)}
@@ -206,6 +289,9 @@ class VisionScheduler:
'capture_count': self.capture_count,
'last_capture_time': self.last_capture_time,
'last_analyze_time': self.last_analyze_time,
'model_calls': self.model_calls,
'local_analyses': self.local_analyses,
'local_stats': self.local_analyzer.get_stats(),
'recent_errors': self.errors[-5:] if self.errors else []
}