From 2e89a910a10af7d6dfa86d323654ae5cbca50055 Mon Sep 17 00:00:00 2001 From: Wang_Run_Ze Date: Fri, 27 Jun 2025 17:10:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=20app.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 734 --------------------------------------------------------- 1 file changed, 734 deletions(-) delete mode 100644 app.py diff --git a/app.py b/app.py deleted file mode 100644 index 32ec99d..0000000 --- a/app.py +++ /dev/null @@ -1,734 +0,0 @@ -from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_from_directory -import os -import shutil -import time -import threading -import json -from datetime import datetime -from pathlib import Path -from flask import Flask, render_template, request, redirect, url_for, flash, jsonify -from werkzeug.utils import secure_filename -from config import config -from batch_detector import BatchVideoDetector - -app = Flask(__name__) -app.secret_key = 'road_damage_detection_system' -app.config['MAX_CONTENT_LENGTH'] = 500 * 1024 * 1024 # 500MB max upload size - -# 确保必要的文件夹存在 -for folder in [config.INPUT_FOLDER, config.OUTPUT_FOLDER, config.LOG_FOLDER, 'models', 'static/css']: - os.makedirs(folder, exist_ok=True) - -# 全局变量用于跟踪检测进度和状态 -detection_progress = 0 -detection_status = "idle" # idle, running, completed, error -detection_thread = None -current_detection_info = {} -detection_start_time = None -detection_results = None -current_report_path = None -current_video_name = "" -total_videos = 0 -current_video_index = 0 -current_frame_count = 0 -total_frame_count = 0 - -# 允许的文件扩展名 -ALLOWED_VIDEO_EXTENSIONS = {ext.lstrip('.') for ext in config.SUPPORTED_VIDEO_FORMATS} -ALLOWED_MODEL_EXTENSIONS = {'pt', 'pth', 'weights'} - -def allowed_file(filename, allowed_extensions): - return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions - -# 主页 -@app.route('/', methods=['GET', 'POST']) -def index(): - global detection_thread, detection_status, detection_progress, detection_results, current_report_path, detection_start_time - - # 处理POST请求(开始检测) - if request.method == 'POST': - if detection_status == "running": - flash('已有检测任务正在运行', 'warning') - else: - # 获取选择的模型和置信度阈值 - model_name = request.form.get('model_select') - confidence_threshold = float(request.form.get('confidence_threshold', 0.25)) - - # 检查是否有视频文件 - videos = [f for f in os.listdir(config.INPUT_FOLDER) - if allowed_file(f, ALLOWED_VIDEO_EXTENSIONS)] - if not videos: - flash('输入文件夹中没有视频文件,请先上传视频', 'danger') - elif not model_name: - flash('请选择一个检测模型', 'danger') - else: - model_path = os.path.join('models', model_name) - if not os.path.exists(model_path): - flash(f'模型文件 {model_name} 不存在', 'danger') - else: - # 更新配置 - config.MODEL_PATH = model_path - config.CONFIDENCE_THRESHOLD = confidence_threshold - - # 重置检测状态和进度 - detection_progress = 0 - detection_status = "running" - detection_results = None - current_report_path = None - detection_start_time = datetime.now() - - # 启动检测线程 - detection_thread = threading.Thread(target=run_detection) - detection_thread.daemon = True - detection_thread.start() - - flash('检测已开始,请等待结果', 'info') - - # 获取可用的模型列表 - print(f"models文件夹路径: {os.path.abspath('models')}") - print(f"models文件夹存在: {os.path.exists('models')}") - if os.path.exists('models'): - all_files = os.listdir('models') - print(f"models文件夹中的所有文件: {all_files}") - for f in all_files: - print(f"检查文件 {f}: allowed_file结果 = {allowed_file(f, ALLOWED_MODEL_EXTENSIONS)}") - models = [f for f in os.listdir('models') if allowed_file(f, ALLOWED_MODEL_EXTENSIONS)] - print(f"找到的模型文件: {models}") - - # 获取输入文件夹中的视频文件 - videos = [f for f in os.listdir(config.INPUT_FOLDER) - if allowed_file(f, ALLOWED_VIDEO_EXTENSIONS)] - - # 获取检测报告列表 - reports = [] - if os.path.exists(config.OUTPUT_FOLDER): - for report_file in os.listdir(config.OUTPUT_FOLDER): - if report_file.startswith('batch_detection_report_') and report_file.endswith('.json'): - report_path = os.path.join(config.OUTPUT_FOLDER, report_file) - try: - with open(report_path, 'r', encoding='utf-8') as f: - report_data = json.load(f) - reports.append({ - 'id': report_file, - 'timestamp': report_data.get('timestamp', '未知'), - 'video_count': report_data.get('statistics', {}).get('processed_videos', 0), - 'total_frames': report_data.get('statistics', {}).get('total_frames', 0), - 'detected_frames': report_data.get('statistics', {}).get('detected_frames', 0) - }) - except Exception as e: - print(f"Error loading report {report_path}: {e}") - - # 按时间戳排序,最新的在前面 - reports.sort(key=lambda x: x['timestamp'], reverse=True) - - return render_template('index.html', - models=models, - videos=videos, - reports=reports, - detection_status=detection_status, - detection_progress=detection_progress, - detection_in_progress=(detection_status == "running"), - confidence_threshold=config.CONFIDENCE_THRESHOLD, - config=config) - -# 上传模型 -@app.route('/upload_model', methods=['POST']) -def upload_model(): - if 'model_file' not in request.files: - flash('没有选择文件', 'danger') - return redirect(url_for('index')) - - file = request.files['model_file'] - if file.filename == '': - flash('没有选择文件', 'danger') - return redirect(url_for('index')) - - if file and allowed_file(file.filename, ALLOWED_MODEL_EXTENSIONS): - filename = secure_filename(file.filename) - model_path = os.path.join('models', filename) - file.save(model_path) - - # 立即检测模型信息并缓存 - try: - print(f"正在分析上传的模型: {filename}") - from ultralytics import YOLO - model = YOLO(model_path) - - # 保存模型信息到JSON文件 - model_info = { - 'filename': filename, - 'upload_time': datetime.now().isoformat(), - 'classes': dict(model.names) if hasattr(model, 'names') else {}, - 'class_count': len(model.names) if hasattr(model, 'names') else 0, - 'model_type': 'YOLO', - 'analyzed': True - } - - # 创建模型信息文件 - info_path = os.path.join('models', f"{os.path.splitext(filename)[0]}_info.json") - with open(info_path, 'w', encoding='utf-8') as f: - import json - json.dump(model_info, f, ensure_ascii=False, indent=2) - - # 显示模型分析结果 - if hasattr(model, 'names'): - scene_types = list(model.names.values()) - flash(f'模型 {filename} 上传成功!检测到 {len(scene_types)} 种场景类型: {", ".join(scene_types)}', 'success') - else: - flash(f'模型 {filename} 上传成功!', 'success') - - except Exception as e: - print(f"模型分析失败: {e}") - flash(f'模型 {filename} 上传成功,但分析模型信息时出错: {str(e)}', 'warning') - else: - flash('不支持的文件类型', 'danger') - - return redirect(url_for('index')) - -# 获取模型列表 -@app.route('/get_models', methods=['GET']) -def get_models(): - models = [] - model_files = [f for f in os.listdir('models') if allowed_file(f, ALLOWED_MODEL_EXTENSIONS)] - - for model_file in model_files: - model_info = { - 'filename': model_file, - 'analyzed': False, - 'classes': {}, - 'class_count': 0, - 'scene_types': [] - } - - # 尝试读取模型信息文件 - info_path = os.path.join('models', f"{os.path.splitext(model_file)[0]}_info.json") - if os.path.exists(info_path): - try: - with open(info_path, 'r', encoding='utf-8') as f: - import json - cached_info = json.load(f) - model_info.update(cached_info) - model_info['scene_types'] = list(cached_info.get('classes', {}).values()) - except Exception as e: - print(f"读取模型信息文件失败: {e}") - - models.append(model_info) - - return jsonify(models) - -# 删除模型 -@app.route('/delete_model/', methods=['POST']) -def delete_model(filename): - try: - model_path = os.path.join('models', filename) - info_path = os.path.join('models', f"{os.path.splitext(filename)[0]}_info.json") - - deleted_files = [] - if os.path.exists(model_path): - os.remove(model_path) - deleted_files.append('模型文件') - - # 同时删除模型信息文件 - if os.path.exists(info_path): - os.remove(info_path) - deleted_files.append('信息文件') - - if deleted_files: - flash(f'模型 {filename} 及其{"、".join(deleted_files)}已删除', 'success') - else: - flash(f'模型 {filename} 不存在', 'warning') - except Exception as e: - flash(f'删除模型时出错: {str(e)}', 'danger') - - return redirect(url_for('index')) - -# 上传视频 -@app.route('/upload_video', methods=['POST']) -def upload_video(): - if 'video_files[]' not in request.files: - flash('没有选择文件', 'danger') - return redirect(url_for('index')) - - files = request.files.getlist('video_files[]') - if not files or files[0].filename == '': - flash('没有选择文件', 'danger') - return redirect(url_for('index')) - - success_count = 0 - error_count = 0 - - for file in files: - if file and allowed_file(file.filename, ALLOWED_VIDEO_EXTENSIONS): - filename = secure_filename(file.filename) - file.save(os.path.join(config.INPUT_FOLDER, filename)) - success_count += 1 - else: - error_count += 1 - - if success_count > 0: - flash(f'成功上传 {success_count} 个视频文件', 'success') - if error_count > 0: - flash(f'{error_count} 个文件上传失败(不支持的文件类型)', 'warning') - - return redirect(url_for('index')) - -# 获取视频列表 -@app.route('/get_videos', methods=['GET']) -def get_videos(): - videos = [f for f in os.listdir(config.INPUT_FOLDER) if allowed_file(f, ALLOWED_VIDEO_EXTENSIONS)] - return jsonify(videos) - -# 删除视频 -@app.route('/delete_video/', methods=['POST']) -def delete_video(filename): - try: - video_path = os.path.join(config.INPUT_FOLDER, filename) - if os.path.exists(video_path): - os.remove(video_path) - flash(f'视频 {filename} 已删除', 'success') - else: - flash(f'视频 {filename} 不存在', 'warning') - except Exception as e: - flash(f'删除视频时出错: {str(e)}', 'danger') - - return redirect(url_for('index')) - -# 清空输入文件夹 -@app.route('/clear_input_folder', methods=['POST']) -def clear_input_folder(): - try: - for filename in os.listdir(config.INPUT_FOLDER): - file_path = os.path.join(config.INPUT_FOLDER, filename) - if os.path.isfile(file_path): - os.unlink(file_path) - flash('输入文件夹已清空', 'success') - except Exception as e: - flash(f'清空文件夹时出错: {str(e)}', 'danger') - - return redirect(url_for('index')) - -# 开始检测 -@app.route('/start_detection', methods=['POST']) -def start_detection(): - global detection_thread, detection_status, detection_progress, detection_results, current_report_path, detection_start_time - - print("收到检测请求") - print(f"当前检测状态: {detection_status}") - - if detection_status == "running": - flash('已有检测任务正在运行', 'warning') - return redirect(url_for('index')) - - # 获取选择的模型和置信度阈值 - model_name = request.form.get('model_select') - confidence_threshold = float(request.form.get('confidence_threshold', 0.25)) - - print(f"接收到的模型名称: {model_name}") - print(f"接收到的置信度阈值: {confidence_threshold}") - - # 检查是否有视频文件 - videos = [f for f in os.listdir(config.INPUT_FOLDER) - if allowed_file(f, ALLOWED_VIDEO_EXTENSIONS)] - if not videos: - flash('输入文件夹中没有视频文件,请先上传视频', 'danger') - return redirect(url_for('index')) - - # 检查模型文件是否存在 - if not model_name: - flash('请选择一个检测模型', 'danger') - return redirect(url_for('index')) - - model_path = os.path.join('models', model_name) - if not os.path.exists(model_path): - flash(f'模型文件 {model_name} 不存在', 'danger') - return redirect(url_for('index')) - - # 更新配置 - config.MODEL_PATH = model_path - config.CONFIDENCE_THRESHOLD = confidence_threshold - - # 重置检测状态和进度 - detection_progress = 0 - detection_status = "running" - detection_results = None - current_report_path = None - detection_start_time = datetime.now() - - # 重置进度相关变量 - global current_video_name, total_videos, current_video_index, current_frame_count, total_frame_count - current_video_name = "" - total_videos = len(videos) - current_video_index = 0 - current_frame_count = 0 - total_frame_count = 0 - - # 启动检测线程 - detection_thread = threading.Thread(target=run_detection) - detection_thread.daemon = True - detection_thread.start() - - flash('检测已开始,请等待结果', 'info') - return redirect(url_for('index')) - -# 检测线程函数 -def run_detection(): - global detection_progress, detection_status, detection_results, current_report_path, current_video_name, current_video_index, current_frame_count - - try: - print(f"开始检测,模型路径: {config.MODEL_PATH}") - print(f"置信度阈值: {config.CONFIDENCE_THRESHOLD}") - print(f"输入文件夹: {config.INPUT_FOLDER}") - - # 获取视频列表 - videos = [f for f in os.listdir(config.INPUT_FOLDER) - if any(f.endswith(ext) for ext in config.SUPPORTED_VIDEO_FORMATS)] - print(f"找到视频文件: {videos}") - - # 创建检测报告基本信息 - timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') - - # 创建检测器实例 - print("正在创建检测器实例...") - detector = BatchVideoDetector( - model_path=config.MODEL_PATH, - confidence=config.CONFIDENCE_THRESHOLD - ) - print("检测器实例创建成功") - - # 显示模型检测能力信息 - print("\n=== 网页端模型检测分析 ===") - if hasattr(detector.model, 'names'): - model_classes = list(detector.model.names.values()) - print(f"当前模型可检测场景: {model_classes}") - print(f"使用的置信度阈值: {config.CONFIDENCE_THRESHOLD}") - print("=" * 40) - - # 设置进度回调 - def progress_callback(status, progress, video_name=None, video_index=None, frame_count=None): - global detection_status, detection_progress, current_video_name, current_video_index, current_frame_count - detection_status = status - detection_progress = progress - - # 更新详细进度信息 - if video_name: - current_video_name = video_name - if video_index is not None: - current_video_index = video_index - if frame_count is not None: - current_frame_count = frame_count - - print(f"进度更新: {status} - {progress}%") - if video_name: - print(f"当前视频: {video_name} ({video_index}/{total_videos})") - if frame_count is not None: - print(f"已处理帧数: {frame_count}") - - detector.set_progress_callback(progress_callback) - - # 执行检测 - detection_status = "开始检测视频..." - report_path = detector.process_all_videos(config.INPUT_FOLDER, config.OUTPUT_FOLDER) - - # 读取检测结果 - if report_path and os.path.exists(report_path): - with open(report_path, 'r', encoding='utf-8') as f: - detection_results = json.load(f) - - current_report_path = report_path - detection_status = "检测完成" - detection_progress = 100 - else: - detection_status = "检测完成,但未生成报告" - detection_progress = 100 - except Exception as e: - detection_status = f"检测失败: {str(e)}" - print(f"检测异常: {str(e)}") - import traceback - traceback.print_exc() - finally: - # 如果状态仍为running,则更新为失败 - if detection_status == "running": - detection_status = "失败" - # 确保进度显示为100% - if detection_progress < 100: - detection_progress = 100 - -# 获取检测进度 -@app.route('/detection_progress') -def get_detection_progress(): - # 计算检测时长 - elapsed_time = "" - if detection_start_time and detection_status == "running": - elapsed_seconds = (datetime.now() - detection_start_time).total_seconds() - minutes, seconds = divmod(elapsed_seconds, 60) - elapsed_time = f"{int(minutes)}分{int(seconds)}秒" - - # 构建详细状态信息 - detailed_status = detection_status - if detection_status == "running" and current_video_name: - detailed_status = f"正在处理: {current_video_name} ({current_video_index}/{total_videos})" - if current_frame_count > 0: - detailed_status += f" - 已处理帧数: {current_frame_count}" - - return jsonify({ - 'in_progress': detection_status == "running", - 'progress': detection_progress, - 'status': detailed_status, - 'elapsed_time': elapsed_time, - 'current_video': current_video_name, - 'current_video_index': current_video_index, - 'total_videos': total_videos, - 'current_frame_count': current_frame_count, - 'total_frame_count': total_frame_count - }) - -# 查看检测报告 -@app.route('/report/') -def view_report(report_filename): - report_path = os.path.join(config.OUTPUT_FOLDER, report_filename) - if not os.path.exists(report_path): - flash('报告文件不存在', 'danger') - return redirect(url_for('index')) - - try: - with open(report_path, 'r', encoding='utf-8') as f: - report_data = json.load(f) - - # 计算检测率 - total_frames = report_data.get('statistics', {}).get('total_frames', 0) - detected_frames = report_data.get('statistics', {}).get('detected_frames', 0) - detection_rate = 0 - if total_frames > 0: - detection_rate = (detected_frames / total_frames) * 100 - - # 为每个视频计算检测率 - for video in report_data.get('videos', []): - video_total_frames = video.get('total_frames', 0) - video_detected_frames = video.get('detected_frames', 0) - if video_total_frames > 0: - video['detection_rate'] = (video_detected_frames / video_total_frames) * 100 - else: - video['detection_rate'] = 0 - - return render_template('report.html', - report=report_data, - report_filename=report_filename, - detection_rate=detection_rate) - - except Exception as e: - flash(f'读取报告时出错: {str(e)}', 'danger') - return redirect(url_for('index')) - -# 查看视频检测结果 -@app.route('/video_results//') -def view_video_results(report_filename, video_name): - report_path = os.path.join(config.OUTPUT_FOLDER, report_filename) - if not os.path.exists(report_path): - flash('报告文件不存在', 'danger') - return redirect(url_for('index')) - - try: - with open(report_path, 'r', encoding='utf-8') as f: - report_data = json.load(f) - - # 查找特定视频的结果 - video_data = None - for video in report_data['videos']: - if video['video_name'] == video_name: - video_data = video - break - - if not video_data: - flash('视频结果不存在', 'danger') - return redirect(url_for('view_report', report_filename=report_filename)) - - # 获取检测到的帧列表 - video_output_dir = os.path.join(config.OUTPUT_FOLDER, os.path.splitext(video_name)[0]) - detected_frames = [] - - if os.path.exists(video_output_dir): - for frame_file in os.listdir(video_output_dir): - if frame_file.startswith('detected_') and frame_file.endswith('.jpg'): - try: - frame_number = int(frame_file.split('_')[1].split('.')[0]) - frame_path = os.path.join(os.path.basename(video_output_dir), frame_file) - - # 获取对应的JSON文件以读取检测信息 - json_file = frame_file.replace('.jpg', '.json') - json_path = os.path.join(video_output_dir, json_file) - detections = [] - - if os.path.exists(json_path): - with open(json_path, 'r', encoding='utf-8') as f: - frame_data = json.load(f) - detections = frame_data.get('detections', []) - - detected_frames.append({ - 'frame_number': frame_number, - 'filename': frame_file, - 'path': frame_path, - 'timestamp': frame_number / video_data.get('fps', 30), # 计算时间戳 - 'detections': detections, - 'detection_count': len(detections) - }) - except Exception as e: - print(f"Error processing frame {frame_file}: {e}") - - # 按帧号排序 - detected_frames.sort(key=lambda x: x['frame_number']) - - # 计算检测率 - total_frames = video_data.get('total_frames', 0) - detected_count = video_data.get('detected_frames', 0) - detection_rate = 0 - if total_frames > 0: - detection_rate = (detected_count / total_frames) * 100 - - # 获取时间线数据 - timeline_data = [] - for frame in detected_frames: - timeline_data.append({ - 'timestamp': frame['timestamp'], - 'frame_number': frame['frame_number'], - 'detection_count': frame['detection_count'] - }) - - return render_template('video_results.html', - report_filename=report_filename, - report=report_data, - video=video_data, - detected_frames=detected_frames, - detection_rate=detection_rate, - timeline_data=json.dumps(timeline_data)) - - except Exception as e: - flash(f'读取视频结果时出错: {str(e)}', 'danger') - return redirect(url_for('view_report', report_filename=report_filename)) - -# 获取检测到的帧图像 -@app.route('/frame/') -def get_frame(frame_path): - directory, filename = os.path.split(frame_path) - return send_from_directory(os.path.join(config.OUTPUT_FOLDER, directory), filename) - -# 删除报告 -@app.route('/delete_report/', methods=['POST']) -def delete_report(report_filename): - report_path = os.path.join(config.OUTPUT_FOLDER, report_filename) - if not os.path.exists(report_path): - flash('报告文件不存在', 'danger') - return redirect(url_for('index')) - - # 读取报告以获取视频文件夹列表 - try: - with open(report_path, 'r', encoding='utf-8') as f: - report_data = json.load(f) - - # 删除每个视频的输出文件夹 - for video in report_data['videos']: - video_output_folder = os.path.join(config.OUTPUT_FOLDER, os.path.splitext(video['video_name'])[0]) - if os.path.exists(video_output_folder) and os.path.isdir(video_output_folder): - shutil.rmtree(video_output_folder) - - # 删除报告文件 - os.remove(report_path) - flash(f'报告 {report_filename} 已删除', 'success') - except Exception as e: - flash(f'删除报告失败: {str(e)}', 'danger') - - return redirect(url_for('index')) - -# 添加BatchVideoDetector的进度回调方法 -def add_progress_callback_to_detector(): - # 检查BatchVideoDetector类是否已有set_progress_callback方法 - if not hasattr(BatchVideoDetector, 'set_progress_callback'): - def set_progress_callback(self, callback): - self.progress_callback = callback - - def update_progress(self, status, progress, video_name=None, video_index=None, frame_count=None): - if hasattr(self, 'progress_callback') and self.progress_callback: - self.progress_callback(status, progress, video_name, video_index, frame_count) - - # 添加方法到BatchVideoDetector类 - BatchVideoDetector.set_progress_callback = set_progress_callback - BatchVideoDetector.update_progress = update_progress - - # 修改process_all_videos方法以支持进度更新 - original_process_all_videos = BatchVideoDetector.process_all_videos - - def process_all_videos_with_progress(self, input_folder="input_videos", output_folder="output_frames"): - self.update_progress("获取视频文件列表", 0) - video_files = self.get_video_files(input_folder) - total_videos = len(video_files) - - if total_videos == 0: - self.update_progress("没有找到视频文件", 100) - return None - - self.update_progress(f"开始处理 {total_videos} 个视频文件", 5) - - # 处理每个视频文件 - for i, video_path in enumerate(video_files, 1): - progress = int(5 + (i / total_videos) * 90) - video_name = video_path.name - self.update_progress(f"处理视频 {i}/{total_videos}: {video_name}", progress, video_name, i) - self.process_video(video_path, output_folder) - - self.update_progress("生成检测报告", 95) - self.stats['end_time'] = datetime.now() - report_path = self.save_final_report(output_folder) - self.print_summary() - self.update_progress("检测完成", 100) - return report_path - - # 修改save_final_report方法以返回报告路径 - original_save_final_report = BatchVideoDetector.save_final_report - - def save_final_report_with_return(self, output_folder): - timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') - report_path = Path(output_folder) / f"batch_detection_report_{timestamp}.json" - - # 计算总处理时间 - total_seconds = 0 - if self.stats['start_time'] and self.stats['end_time']: - total_seconds = (self.stats['end_time'] - self.stats['start_time']).total_seconds() - - # 创建报告数据 - report_data = { - 'timestamp': timestamp, - 'model_path': self.model_path, - 'confidence_threshold': self.confidence, - 'frame_interval_threshold': config.MIN_FRAME_INTERVAL, - 'filter_strategy': '相同场景在指定时间间隔内只保存一次', - 'statistics': { - 'total_videos': self.stats['total_videos'], - 'processed_videos': self.stats['processed_videos'], - 'total_frames': self.stats['total_frames'], - 'detected_frames': self.stats['detected_frames'], - 'filtered_frames': self.stats['filtered_frames'], - 'saved_images': self.stats['saved_images'], - 'processing_time_seconds': total_seconds - }, - 'errors': self.stats['errors'], - 'videos': [] - } - - # 保存为JSON - with open(report_path, 'w', encoding='utf-8') as f: - json.dump(report_data, f, ensure_ascii=False, indent=2) - - print(f"\n批处理报告已保存: {report_path}") - return str(report_path) - - BatchVideoDetector.process_all_videos = process_all_videos_with_progress - BatchVideoDetector.save_final_report = save_final_report_with_return - -# 在应用启动前添加进度回调方法 -add_progress_callback_to_detector() - -if __name__ == '__main__': - # 确保必要的文件夹存在 - for folder in [config.INPUT_FOLDER, config.OUTPUT_FOLDER, config.LOG_FOLDER, 'models', 'static/css']: - os.makedirs(folder, exist_ok=True) - - app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file