Files
mxivideo/python_core/cli/scene_detect.py
2025-07-12 12:45:21 +08:00

871 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
PySceneDetect 场景检测命令行工具 - LangGraph增强版
"""
import json
import time
from pathlib import Path
from typing import Optional, List, Literal, Dict, Any
from enum import Enum
from dataclasses import dataclass, asdict
from python_core.cli.const import progress_reporter
# PySceneDetect 依赖
from scenedetect import open_video, SceneManager
from scenedetect.detectors import ContentDetector, ThresholdDetector
# LangGraph 依赖
from langgraph.graph import StateGraph, START, END
from langgraph.graph.state import CompiledStateGraph
from langgraph.checkpoint.memory import MemorySaver
from langchain_anthropic import ChatAnthropic
class DetectorType(str, Enum):
"""检测器类型"""
CONTENT = "content"
THRESHOLD = "threshold"
ADAPTIVE = "adaptive"
class OutputFormat(str, Enum):
"""输出格式"""
JSON = "json"
CSV = "csv"
TXT = "txt"
@dataclass
class SceneInfo:
"""场景信息"""
index: int
start_time: float
end_time: float
duration: float
start_frame: int
end_frame: int
@dataclass
class DetectionResult:
"""检测结果"""
video_path: str
filename: str
detector_type: str
threshold: float
total_scenes: int
total_duration: float
detection_time: float
scenes: List[SceneInfo]
success: bool
error: Optional[str] = None
# LangGraph 工作流状态
@dataclass
class SceneDetectionWorkflowState:
"""场景检测工作流状态"""
# 输入参数
video_path: str = ""
detector_type: str = "content"
threshold: float = 30.0
min_scene_length: float = 1.0
output_path: Optional[str] = None
output_format: str = "json"
enable_ai_analysis: bool = True
# 工作流状态
current_stage: str = "init"
progress: int = 0
total_steps: int = 5
# 中间结果
video_info: Dict[str, Any] = None
raw_scenes: List[Any] = None
processed_scenes: List[SceneInfo] = None
# 最终结果
detection_result: Optional[DetectionResult] = None
ai_analysis: Optional[str] = None
# 错误处理
errors: List[str] = None
def __post_init__(self):
if self.video_info is None:
self.video_info = {}
if self.raw_scenes is None:
self.raw_scenes = []
if self.processed_scenes is None:
self.processed_scenes = []
if self.errors is None:
self.errors = []
class SceneDetector:
"""场景检测器"""
def __init__(self):
self.supported_formats = {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v'}
# 初始化AI分析器如果可用
try:
self.llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
self.ai_enabled = True
except Exception as e:
progress_reporter.warning(f"⚠️ AI分析器初始化失败: {e}")
self.ai_enabled = False
def detect_scenes(self, video_path: Path, detector_type: DetectorType = DetectorType.CONTENT,
threshold: float = 30.0, min_scene_length: float = 1.0) -> DetectionResult:
"""检测单个视频的场景"""
start_time = time.time()
filename = video_path.name
try:
progress_reporter.info(f"🎬 开始检测: {filename}")
# 打开视频文件
video = open_video(str(video_path))
scene_manager = SceneManager()
# 根据类型添加检测器
if detector_type == DetectorType.CONTENT:
scene_manager.add_detector(ContentDetector(threshold=threshold))
progress_reporter.info(f"📊 使用内容检测器,阈值: {threshold}")
elif detector_type == DetectorType.THRESHOLD:
scene_manager.add_detector(ThresholdDetector(threshold=threshold))
progress_reporter.info(f"📊 使用阈值检测器,阈值: {threshold}")
elif detector_type == DetectorType.ADAPTIVE:
# 自适应:同时使用两种检测器
scene_manager.add_detector(ContentDetector(threshold=threshold))
scene_manager.add_detector(ThresholdDetector(threshold=threshold * 0.8))
progress_reporter.info(f"📊 使用自适应检测器,阈值: {threshold}")
# 开始检测
progress_reporter.info("🔍 正在分析视频帧...")
scene_manager.detect_scenes(video)
scene_list = scene_manager.get_scene_list()
progress_reporter.info(f"✅ 检测到 {len(scene_list)} 个场景")
# 获取视频信息
fps = video.frame_rate
total_duration = video.duration.get_seconds()
progress_reporter.info(f"📊 视频信息: {fps:.2f}fps, {total_duration:.2f}")
# 构建场景信息
scenes = []
if not scene_list:
# 如果没有检测到场景变化,整个视频就是一个场景
scene_info = SceneInfo(
index=0,
start_time=0.0,
end_time=total_duration,
duration=total_duration,
start_frame=0,
end_frame=int(total_duration * fps) if fps > 0 else 0
)
scenes.append(scene_info)
progress_reporter.info(f"📝 无场景变化,整个视频作为单一场景: {total_duration:.2f}")
else:
# 处理检测到的场景
for start_time_scene, end_time_scene in scene_list:
start_seconds = start_time_scene.get_seconds()
end_seconds = end_time_scene.get_seconds()
duration = end_seconds - start_seconds
# 跳过太短的场景
if duration < min_scene_length:
continue
scene_info = SceneInfo(
index=len(scenes),
start_time=start_seconds,
end_time=end_seconds,
duration=duration,
start_frame=start_time_scene.get_frames(),
end_frame=end_time_scene.get_frames()
)
scenes.append(scene_info)
detection_time = time.time() - start_time
result = DetectionResult(
video_path=str(video_path),
filename=filename,
detector_type=detector_type.value,
threshold=threshold,
total_scenes=len(scenes),
total_duration=total_duration,
detection_time=detection_time,
scenes=scenes,
success=True
)
progress_reporter.success(f"🎯 检测完成: {len(scenes)} 个场景,耗时 {detection_time:.2f}")
return result
except Exception as e:
detection_time = time.time() - start_time
error_msg = str(e)
progress_reporter.error(f"❌ 检测失败: {error_msg}")
return DetectionResult(
video_path=str(video_path),
filename=filename,
detector_type=detector_type.value,
threshold=threshold,
total_scenes=0,
total_duration=0.0,
detection_time=detection_time,
scenes=[],
success=False,
error=error_msg
)
def batch_detect(self, input_directory: Path, detector_type: DetectorType = DetectorType.CONTENT,
threshold: float = 30.0, recursive: bool = False) -> List[DetectionResult]:
"""批量检测场景"""
progress_reporter.info(f"📦 开始批量检测: {input_directory}")
# 扫描视频文件
video_files = self._scan_video_files(input_directory, recursive)
if not video_files:
progress_reporter.warning("⚠️ 未找到视频文件")
return []
progress_reporter.info(f"📋 找到 {len(video_files)} 个视频文件")
results = []
for i, video_file in enumerate(video_files):
progress_reporter.info(f"📊 处理进度: {i+1}/{len(video_files)} - {video_file.name}")
result = self.detect_scenes(video_file, detector_type, threshold)
results.append(result)
successful = len([r for r in results if r.success])
progress_reporter.success(f"🎉 批量检测完成: {successful}/{len(results)} 成功")
return results
def compare_detectors(self, video_path: Path, thresholds: List[float] = None) -> dict:
"""比较不同检测器效果"""
if thresholds is None:
thresholds = [20.0, 30.0, 40.0]
progress_reporter.info(f"🔬 开始检测器比较: {video_path.name}")
detectors = [DetectorType.CONTENT, DetectorType.THRESHOLD, DetectorType.ADAPTIVE]
results = []
total_tests = len(detectors) * len(thresholds)
current_test = 0
for detector in detectors:
for threshold in thresholds:
current_test += 1
progress_reporter.info(f"🧪 测试 {current_test}/{total_tests}: {detector.value} (阈值: {threshold})")
result = self.detect_scenes(video_path, detector, threshold)
results.append({
"detector": detector.value,
"threshold": threshold,
"scenes": result.total_scenes,
"duration": result.total_duration,
"detection_time": result.detection_time,
"success": result.success,
"error": result.error
})
# 分析结果
analysis = self._analyze_comparison(results)
comparison_result = {
"video_path": str(video_path),
"total_tests": total_tests,
"results": results,
"analysis": analysis
}
progress_reporter.success("🔬 检测器比较完成")
return comparison_result
def _scan_video_files(self, directory: Path, recursive: bool = False) -> List[Path]:
"""扫描视频文件"""
video_files = []
if recursive:
for ext in self.supported_formats:
video_files.extend(directory.rglob(f"*{ext}"))
else:
for ext in self.supported_formats:
video_files.extend(directory.glob(f"*{ext}"))
return sorted(video_files)
def _analyze_comparison(self, results: List[dict]) -> dict:
"""分析比较结果"""
successful_results = [r for r in results if r["success"]]
if not successful_results:
return {"message": "所有测试都失败了"}
# 按检测器分组
by_detector = {}
for result in successful_results:
detector = result["detector"]
if detector not in by_detector:
by_detector[detector] = []
by_detector[detector].append(result)
# 分析每个检测器
detector_analysis = {}
for detector, detector_results in by_detector.items():
avg_scenes = sum(r["scenes"] for r in detector_results) / len(detector_results)
avg_time = sum(r["detection_time"] for r in detector_results) / len(detector_results)
detector_analysis[detector] = {
"average_scenes": avg_scenes,
"average_detection_time": avg_time,
"test_count": len(detector_results)
}
# 推荐最佳检测器
best_detector = max(detector_analysis.keys(),
key=lambda d: detector_analysis[d]["average_scenes"])
return {
"total_successful": len(successful_results),
"detector_analysis": detector_analysis,
"best_detector": best_detector,
"recommendation": f"推荐使用 {best_detector} 检测器"
}
def save_results(self, results, output_path: Path, format: OutputFormat = OutputFormat.JSON):
"""保存检测结果"""
output_path.parent.mkdir(parents=True, exist_ok=True)
if format == OutputFormat.JSON:
self._save_json(results, output_path)
elif format == OutputFormat.CSV:
self._save_csv(results, output_path)
elif format == OutputFormat.TXT:
self._save_txt(results, output_path)
progress_reporter.success(f"📄 结果已保存: {output_path}")
def _save_json(self, results, output_path: Path):
"""保存JSON格式"""
if isinstance(results, list):
# 批量结果
data = [asdict(result) for result in results]
else:
# 单个结果或比较结果
if hasattr(results, '__dict__'):
data = asdict(results)
else:
data = results
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
def _save_csv(self, results, output_path: Path):
"""保存CSV格式"""
import csv
with open(output_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
if isinstance(results, list) and results:
# 批量结果
writer.writerow(['filename', 'detector', 'threshold', 'scenes', 'duration', 'detection_time', 'success'])
for result in results:
writer.writerow([
result.filename,
result.detector_type,
result.threshold,
result.total_scenes,
result.total_duration,
result.detection_time,
result.success
])
def _save_txt(self, results, output_path: Path):
"""保存文本格式"""
with open(output_path, 'w', encoding='utf-8') as f:
f.write("PySceneDetect 场景检测结果\n")
f.write("=" * 50 + "\n\n")
if isinstance(results, list):
# 批量结果
for result in results:
f.write(f"文件: {result.filename}\n")
f.write(f" 检测器: {result.detector_type}\n")
f.write(f" 阈值: {result.threshold}\n")
f.write(f" 场景数: {result.total_scenes}\n")
f.write(f" 总时长: {result.total_duration:.2f}\n")
f.write(f" 检测时间: {result.detection_time:.2f}\n")
f.write(f" 状态: {'成功' if result.success else '失败'}\n")
if result.scenes:
f.write(" 场景列表:\n")
for scene in result.scenes:
f.write(f" 场景 {scene.index}: {scene.start_time:.2f}s - {scene.end_time:.2f}s ({scene.duration:.2f}s)\n")
f.write("\n")
# ==================== LangGraph 工作流方法 ====================
def create_detection_workflow(self) -> Optional[CompiledStateGraph]:
"""创建场景检测工作流"""
# 定义工作流节点
def validate_input(state: SceneDetectionWorkflowState) -> Dict[str, Any]:
"""验证输入参数"""
progress_reporter.info("🔍 验证输入参数...")
video_path = Path(state.video_path)
errors = []
# 验证文件存在
if not video_path.exists():
errors.append(f"视频文件不存在: {video_path}")
# 验证文件格式
if video_path.suffix.lower() not in self.supported_formats:
errors.append(f"不支持的文件格式: {video_path.suffix}")
# 验证参数范围
if not (0 <= state.threshold <= 100):
errors.append(f"阈值超出范围 (0-100): {state.threshold}")
if state.min_scene_length < 0:
errors.append(f"最小场景长度不能为负数: {state.min_scene_length}")
return {
"current_stage": "validated" if not errors else "error",
"progress": 1,
"errors": errors
}
def extract_video_info(state: SceneDetectionWorkflowState) -> Dict[str, Any]:
"""提取视频信息"""
progress_reporter.info("📊 提取视频信息...")
try:
import cv2
cap = cv2.VideoCapture(state.video_path)
if not cap.isOpened():
raise Exception("无法打开视频文件")
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
duration = frame_count / fps if fps > 0 else 0
cap.release()
video_info = {
"fps": fps,
"frame_count": frame_count,
"width": width,
"height": height,
"duration": duration,
"resolution": f"{width}x{height}"
}
progress_reporter.info(f"📹 视频信息: {video_info['resolution']}, {fps:.2f}fps, {duration:.2f}s")
return {
"current_stage": "info_extracted",
"progress": 2,
"video_info": video_info
}
except Exception as e:
return {
"current_stage": "error",
"errors": state.errors + [f"提取视频信息失败: {e}"]
}
def detect_scenes(state: SceneDetectionWorkflowState) -> Dict[str, Any]:
"""执行场景检测"""
progress_reporter.info("🎯 执行场景检测...")
try:
# 使用现有的检测逻辑
result = self.detect_scenes(
Path(state.video_path),
DetectorType(state.detector_type),
state.threshold,
state.min_scene_length
)
return {
"current_stage": "scenes_detected",
"progress": 3,
"detection_result": result,
"processed_scenes": result.scenes
}
except Exception as e:
return {
"current_stage": "error",
"errors": state.errors + [f"场景检测失败: {e}"]
}
def analyze_with_ai(state: SceneDetectionWorkflowState) -> Dict[str, Any]:
"""AI分析场景结果"""
if not self.ai_enabled or not state.enable_ai_analysis:
progress_reporter.info("⚠️ AI分析已禁用跳过此步骤")
return {
"current_stage": "analysis_skipped",
"progress": 4,
"ai_analysis": "AI分析已禁用"
}
progress_reporter.info("🧠 AI分析场景结果...")
try:
result = state.detection_result
video_info = state.video_info
analysis_prompt = f"""
请分析以下视频场景检测结果:
视频信息:
- 文件: {result.filename}
- 分辨率: {video_info.get('resolution', 'Unknown')}
- 时长: {result.total_duration:.2f}
- 帧率: {video_info.get('fps', 0):.2f}fps
检测结果:
- 检测器: {result.detector_type}
- 阈值: {result.threshold}
- 场景数: {result.total_scenes}
- 检测时间: {result.detection_time:.2f}
场景详情:
{self._format_scenes_for_ai(result.scenes)}
请提供:
1. 场景分布分析
2. 检测质量评估
3. 参数优化建议
4. 潜在问题识别
"""
response = self.llm.invoke([{"role": "user", "content": analysis_prompt}])
return {
"current_stage": "ai_analyzed",
"progress": 4,
"ai_analysis": response.content
}
except Exception as e:
progress_reporter.warning(f"⚠️ AI分析失败: {e}")
return {
"current_stage": "analysis_failed",
"progress": 4,
"ai_analysis": f"AI分析失败: {e}"
}
def finalize_results(state: SceneDetectionWorkflowState) -> Dict[str, Any]:
"""整理最终结果"""
progress_reporter.info("📋 整理最终结果...")
# 保存结果(如果指定了输出路径)
if state.output_path and state.detection_result:
try:
output_path = Path(state.output_path)
output_format = OutputFormat(state.output_format)
self.save_results(state.detection_result, output_path, output_format)
except Exception as e:
progress_reporter.warning(f"⚠️ 保存结果失败: {e}")
return {
"current_stage": "completed",
"progress": 5
}
# 路由函数
def route_next_step(state: SceneDetectionWorkflowState) -> Literal["extract_info", "detect", "analyze", "finalize", "error"]:
if state.errors:
return "error"
elif state.current_stage == "validated":
return "extract_info"
elif state.current_stage == "info_extracted":
return "detect"
elif state.current_stage == "scenes_detected":
return "analyze"
elif state.current_stage in ["ai_analyzed", "analysis_skipped", "analysis_failed"]:
return "finalize"
else:
return "error"
def handle_error(state: SceneDetectionWorkflowState) -> Dict[str, Any]:
"""处理错误"""
error_msg = "; ".join(state.errors)
progress_reporter.error(f"❌ 工作流错误: {error_msg}")
return {"current_stage": "failed"}
# 构建工作流图
workflow = StateGraph(SceneDetectionWorkflowState)
# 添加节点
workflow.add_node("validate", validate_input)
workflow.add_node("extract_info", extract_video_info)
workflow.add_node("detect", detect_scenes)
workflow.add_node("analyze", analyze_with_ai)
workflow.add_node("finalize", finalize_results)
workflow.add_node("error", handle_error)
# 添加边
workflow.add_edge(START, "validate")
workflow.add_conditional_edges("validate", route_next_step)
workflow.add_conditional_edges("extract_info", route_next_step)
workflow.add_conditional_edges("detect", route_next_step)
workflow.add_conditional_edges("analyze", route_next_step)
workflow.add_edge("finalize", END)
workflow.add_edge("error", END)
# 编译工作流
memory = MemorySaver()
return workflow.compile(checkpointer=memory)
def _format_scenes_for_ai(self, scenes: List[SceneInfo]) -> str:
"""格式化场景信息供AI分析"""
if not scenes:
return "无场景数据"
formatted = []
for scene in scenes[:10]: # 只显示前10个场景
formatted.append(
f"场景 {scene.index}: {scene.start_time:.2f}s - {scene.end_time:.2f}s "
f"(时长: {scene.duration:.2f}s)"
)
if len(scenes) > 10:
formatted.append(f"... 还有 {len(scenes) - 10} 个场景")
return "\n".join(formatted)
def detect_with_workflow(self, video_path: Path, detector_type: DetectorType = DetectorType.CONTENT,
threshold: float = 30.0, min_scene_length: float = 1.0,
output_path: Optional[Path] = None, output_format: OutputFormat = OutputFormat.JSON,
enable_ai_analysis: bool = True) -> Dict[str, Any]:
"""使用LangGraph工作流进行场景检测"""
# 创建工作流
workflow = self.create_detection_workflow()
if not workflow:
raise Exception("无法创建工作流")
# 初始化状态
initial_state = SceneDetectionWorkflowState(
video_path=str(video_path),
detector_type=detector_type.value,
threshold=threshold,
min_scene_length=min_scene_length,
output_path=str(output_path) if output_path else None,
output_format=output_format.value,
enable_ai_analysis=enable_ai_analysis # 使用参数
)
# 执行工作流
config = {"configurable": {"thread_id": f"detection_{int(time.time())}"}}
try:
final_state = workflow.invoke(initial_state, config)
return {
"detection_result": final_state.get("detection_result"),
"ai_analysis": final_state.get("ai_analysis"),
"video_info": final_state.get("video_info"),
"workflow_state": final_state.get("current_stage"),
"errors": final_state.get("errors", [])
}
except Exception as e:
progress_reporter.error(f"❌ 工作流执行失败: {e}")
raise
# ==================== JSON-RPC 方法注册 ====================
def register_jsonrpc_methods(self):
"""注册JSON-RPC方法到全局注册器"""
from python_core.utils.jsonrpc_enhanced import method_registry
# 注册方法到全局注册器
method_registry.register_function(self.jsonrpc_detect_scenes, "scene.detect")
method_registry.register_function(self.jsonrpc_detect_with_workflow, "scene.detect_workflow")
method_registry.register_function(self.jsonrpc_get_video_info, "scene.get_video_info")
method_registry.register_function(self.jsonrpc_batch_detect, "scene.batch_detect")
def jsonrpc_detect_scenes(self, video_path: str, detector_type: str = "content",
threshold: float = 30.0, min_scene_length: float = 1.0) -> Dict[str, Any]:
"""JSON-RPC方法场景检测"""
try:
result = self.detect_scenes(
Path(video_path),
DetectorType(detector_type),
threshold,
min_scene_length
)
return {
"success": result.success,
"filename": result.filename,
"detector_type": result.detector_type,
"threshold": result.threshold,
"total_scenes": result.total_scenes,
"total_duration": result.total_duration,
"detection_time": result.detection_time,
"scenes": [asdict(scene) for scene in result.scenes],
"error": result.error
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
def jsonrpc_detect_with_workflow(self, video_path: str, detector_type: str = "content",
threshold: float = 30.0, min_scene_length: float = 1.0,
output_path: Optional[str] = None, output_format: str = "json",
enable_ai_analysis: bool = True) -> Dict[str, Any]:
"""JSON-RPC方法工作流场景检测"""
try:
output_path_obj = Path(output_path) if output_path else None
result = self.detect_with_workflow(
Path(video_path),
DetectorType(detector_type),
threshold,
min_scene_length,
output_path_obj,
OutputFormat(output_format),
enable_ai_analysis
)
# 序列化结果
serialized_result = {}
for key, value in result.items():
if key == "detection_result" and value:
serialized_result[key] = {
"success": value.success,
"filename": value.filename,
"detector_type": value.detector_type,
"threshold": value.threshold,
"total_scenes": value.total_scenes,
"total_duration": value.total_duration,
"detection_time": value.detection_time,
"scenes": [asdict(scene) for scene in value.scenes],
"error": value.error
}
else:
serialized_result[key] = value
return serialized_result
except Exception as e:
return {
"success": False,
"error": str(e)
}
def get_video_info(self, video_path: Path) -> Dict[str, Any]:
"""获取视频信息"""
try:
import cv2
cap = cv2.VideoCapture(str(video_path))
if not cap.isOpened():
raise Exception("无法打开视频文件")
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
duration = frame_count / fps if fps > 0 else 0
cap.release()
return {
"filename": video_path.name,
"fps": fps,
"frame_count": frame_count,
"width": width,
"height": height,
"duration": duration,
"resolution": f"{width}x{height}",
"file_size": video_path.stat().st_size if video_path.exists() else 0
}
except Exception as e:
raise Exception(f"获取视频信息失败: {e}")
def jsonrpc_get_video_info(self, video_path: str) -> Dict[str, Any]:
"""JSON-RPC方法获取视频信息"""
try:
info = self.get_video_info(Path(video_path))
return {
"success": True,
"info": info
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
def jsonrpc_batch_detect(self, directory: str, detector_type: str = "content",
threshold: float = 30.0, min_scene_length: float = 1.0,
output_dir: Optional[str] = None, output_format: str = "json") -> Dict[str, Any]:
"""JSON-RPC方法批量场景检测"""
try:
output_dir_obj = Path(output_dir) if output_dir else None
results = self.batch_detect(
Path(directory),
DetectorType(detector_type),
threshold,
min_scene_length,
output_dir_obj,
OutputFormat(output_format)
)
# 序列化结果
serialized_results = []
for result in results:
serialized_results.append({
"success": result.success,
"filename": result.filename,
"detector_type": result.detector_type,
"threshold": result.threshold,
"total_scenes": result.total_scenes,
"total_duration": result.total_duration,
"detection_time": result.detection_time,
"scenes": [asdict(scene) for scene in result.scenes],
"error": result.error
})
return {
"success": True,
"results": serialized_results,
"total_processed": len(results)
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
# 创建全局检测器实例
detector = SceneDetector()
# 注册JSON-RPC方法
detector.register_jsonrpc_methods()