weiyi01191 commited on
Commit
a02a090
·
1 Parent(s): e04b291

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +539 -99
app.py CHANGED
@@ -1,14 +1,33 @@
1
  #!/usr/bin/env python3
2
  """
3
- 🎥 Video Content Safety Analysis
4
- 适配ZeroGPU的视频内容安全分析应用
5
  """
6
  import os
7
  import gradio as gr
8
  import torch
9
- from typing import Tuple
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- # 设置中国镜像(如果在中国网络环境)
 
 
 
12
  os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
13
 
14
  # ZeroGPU装饰器
@@ -19,7 +38,6 @@ try:
19
  except ImportError:
20
  print("⚠️ ZeroGPU spaces 不可用,使用CPU模式")
21
  GPU_AVAILABLE = False
22
- # 创建空装饰器
23
  class spaces:
24
  @staticmethod
25
  def GPU(func):
@@ -27,146 +45,568 @@ except ImportError:
27
 
28
  # 全局变量
29
  model = None
30
- processor = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- def load_model():
33
- """加载模型(延迟加载)"""
34
- global model, processor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  if model is not None:
37
- return model, processor
38
 
39
  try:
40
- print("🔄 正在加载模型...")
41
- print("✅ 模型加载成功(模拟)")
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- model = "simulation_model"
44
- processor = "simulation_processor"
45
 
46
- return model, processor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  except Exception as e:
49
  print(f"❌ 模型加载失败: {e}")
50
- return None, None
 
51
 
52
  @spaces.GPU if GPU_AVAILABLE else lambda f: f
53
- def analyze_video_content(video_path: str, instruction: str = "请分析这个视频的内容") -> Tuple[str, str]:
54
- """分析视频内容"""
 
 
 
55
  try:
56
  # 加载模型
57
- model, processor = load_model()
58
- if model is None:
59
- return "❌ 模型加载失败", "无法评估"
60
 
61
- print(f"🔄 正在分析视频: {video_path}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  print(f"📝 分析指令: {instruction}")
63
 
64
- # 模拟分析结果
65
- analysis_result = f"""
66
- 🎬 视频内容分析结果
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
- 📋 基本信息:
69
- - 视频路径: {video_path}
 
70
  - 分析指令: {instruction}
71
 
72
- 🔍 内容分析:
73
- - 检测到的对象: 人物、场景、文字等
74
- - 音频内容: 语音转文字结果
75
- - 情感分析: 积极/中性/消极
76
 
77
- 🛡️ 安全检测:
78
- - 暴力内容: 未检测到
79
- - 不当内容: 未检测到
80
- - 版权问题: 未检测到
81
 
82
- 总体评估: 内容安全,符合平台规范
 
 
 
 
 
 
 
83
  """
84
 
85
- safety_rating = "✅ P3 (安全)"
 
86
 
87
- return analysis_result, safety_rating
88
 
89
  except Exception as e:
90
- error_msg = f"❌ 分析过程中出错: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
91
  return error_msg, "⚠️ 错误"
92
 
93
- def create_interface():
94
- """创建简化的Gradio界面"""
95
 
96
- with gr.Blocks(title="Video Content Safety Analysis") as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- gr.Markdown("# 🎥 智能视频内容安全分析")
99
- gr.Markdown("基于MiniGPT4-Video的多模态视频理解与安全检测系统")
100
 
101
- with gr.Row():
102
- with gr.Column():
103
- gr.Markdown("## 📤 上传视频")
104
-
105
- video_input = gr.Video(label="选择视频文件")
106
-
107
- instruction_input = gr.Textbox(
108
- label="分析指令",
109
- value="请分析这个视频的内容,重点关注是否存在违规内容",
110
- lines=2
111
- )
112
-
113
- analyze_btn = gr.Button("🚀 开始分析")
114
-
115
- with gr.Column():
116
- gr.Markdown("## 📊 分析结果")
117
-
118
- analysis_output = gr.Textbox(
119
- label="详细分析",
120
- lines=10
121
- )
122
-
123
- safety_output = gr.Textbox(
124
- label="安全评级",
125
- lines=1
126
- )
127
-
128
- gr.Markdown("""
129
- ## 💡 使用说明
130
-
131
- 1. 上传视频文件
132
- 2. 输入分析指令
133
- 3. 点击开始分析
134
- 4. 查看分析结果
135
-
136
- ## ⚠️ 注意事项
137
-
138
- - ZeroGPU有60秒运行时间限制
139
- - 建议上传文件小于50MB
140
- - 首次加载模型需要1-2分钟
141
- """)
142
-
143
- # 绑定事件
144
- analyze_btn.click(
145
- fn=analyze_video_content,
146
- inputs=[video_input, instruction_input],
147
- outputs=[analysis_output, safety_output]
148
- )
149
 
150
- return app
151
 
152
  def main():
153
  """主函数"""
154
- print("🚀 启动视频内容安全分析应用")
 
 
155
 
156
- # 检查GPU可用性
157
  if torch.cuda.is_available():
158
  print(f"✅ GPU可用: {torch.cuda.get_device_name(0)}")
159
  else:
160
  print("⚠️ 使用CPU模式")
161
 
162
- # 创建应用
163
- app = create_interface()
 
 
 
 
 
164
 
165
- # 启动应用 - ZeroGPU环境配置
 
 
166
  app.launch(
 
167
  server_name="0.0.0.0",
168
  server_port=7860,
169
- share=True,
170
  show_error=True
171
  )
172
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ 🎥 Video Content Safety Analysis - MiniGPT4-Video + 巨量引擎规则集成版
4
+ 基于MiniGPT4-Video的真实视频内容分析 + 巨量引擎299条禁投规则检测
5
  """
6
  import os
7
  import gradio as gr
8
  import torch
9
+ import gc
10
+ import whisper
11
+ import argparse
12
+ import yaml
13
+ import random
14
+ import numpy as np
15
+ import torch.backends.cudnn as cudnn
16
+ from minigpt4.common.eval_utils import init_model
17
+ from minigpt4.conversation.conversation import CONV_VISION
18
+ import tempfile
19
+ import shutil
20
+ import cv2
21
+ import webvtt
22
+ import moviepy.editor as mp
23
+ from torchvision import transforms
24
+ from datetime import timedelta
25
+ from moviepy.editor import VideoFileClip
26
 
27
+ # 导入巨量引擎禁投规则引擎
28
+ from prohibited_rules import ProhibitedRulesEngine
29
+
30
+ # 设置中国镜像
31
  os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
32
 
33
  # ZeroGPU装饰器
 
38
  except ImportError:
39
  print("⚠️ ZeroGPU spaces 不可用,使用CPU模式")
40
  GPU_AVAILABLE = False
 
41
  class spaces:
42
  @staticmethod
43
  def GPU(func):
 
45
 
46
  # 全局变量
47
  model = None
48
+ vis_processor = None
49
+ whisper_model = None
50
+ args = None
51
+ seed = 42
52
+
53
+ # 初始化巨量引擎规则引擎
54
+ rules_engine = ProhibitedRulesEngine()
55
+ print("✅ 巨量引擎299条禁投规则引擎初始化完成")
56
+
57
+ # ======================== MiniGPT4-Video 核心函数 ========================
58
+
59
+ def format_timestamp(seconds):
60
+ """格式化时间戳为VTT格式"""
61
+ td = timedelta(seconds=seconds)
62
+ total_seconds = int(td.total_seconds())
63
+ milliseconds = int(td.microseconds / 1000)
64
+ hours, remainder = divmod(total_seconds, 3600)
65
+ minutes, seconds = divmod(remainder, 60)
66
+ return f"{hours:02}:{minutes:02}:{seconds:02}.{milliseconds:03}"
67
+
68
+ def extract_video_info(video_path, max_images_length):
69
+ """提取视频信息"""
70
+ clip = VideoFileClip(video_path)
71
+ total_num_frames = int(clip.duration * clip.fps)
72
+ clip.close()
73
+ sampling_interval = int(total_num_frames / max_images_length)
74
+ if sampling_interval == 0:
75
+ sampling_interval = 1
76
+ return sampling_interval, clip.fps
77
+
78
+ def time_to_milliseconds(time_str):
79
+ """将时间格式转换为毫秒"""
80
+ h, m, s = map(float, time_str.split(':'))
81
+ return int((h * 3600 + m * 60 + s) * 1000)
82
+
83
+ def extract_subtitles(subtitle_path):
84
+ """提取字幕"""
85
+ if not subtitle_path or not os.path.exists(subtitle_path):
86
+ return []
87
+
88
+ subtitles = []
89
+ try:
90
+ for caption in webvtt.read(subtitle_path):
91
+ start_ms = time_to_milliseconds(caption.start)
92
+ end_ms = time_to_milliseconds(caption.end)
93
+ text = caption.text.strip().replace('\n', ' ')
94
+ subtitles.append((start_ms, end_ms, text))
95
+ except:
96
+ return []
97
+ return subtitles
98
+
99
+ def find_subtitle(subtitles, frame_count, fps):
100
+ """查找对应帧的字幕"""
101
+ if not subtitles:
102
+ return None
103
+
104
+ frame_time = (frame_count / fps) * 1000
105
+ left, right = 0, len(subtitles) - 1
106
+
107
+ while left <= right:
108
+ mid = (left + right) // 2
109
+ start, end, subtitle_text = subtitles[mid]
110
+ if start <= frame_time <= end:
111
+ return subtitle_text
112
+ elif frame_time < start:
113
+ right = mid - 1
114
+ else:
115
+ left = mid + 1
116
+
117
+ return None
118
+
119
+ def match_frames_and_subtitles(video_path, subtitles, sampling_interval, max_sub_len, fps, max_frames):
120
+ """匹配视频帧和字幕"""
121
+ global vis_processor
122
+
123
+ cap = cv2.VideoCapture(video_path)
124
+ images = []
125
+ frame_count = 0
126
+ img_placeholder = ""
127
+ subtitle_text_in_interval = ""
128
+ history_subtitles = {}
129
+ number_of_words = 0
130
+
131
+ transform = transforms.Compose([
132
+ transforms.ToPILImage(),
133
+ ])
134
+
135
+ while cap.isOpened():
136
+ ret, frame = cap.read()
137
+ if not ret:
138
+ break
139
+
140
+ if len(subtitles) > 0:
141
+ frame_subtitle = find_subtitle(subtitles, frame_count, fps)
142
+ if frame_subtitle and not history_subtitles.get(frame_subtitle, False):
143
+ subtitle_text_in_interval += frame_subtitle + " "
144
+ history_subtitles[frame_subtitle] = True
145
+
146
+ if frame_count % sampling_interval == 0:
147
+ frame = transform(frame[:,:,::-1]) # 转换为RGB
148
+ frame = vis_processor(frame)
149
+ images.append(frame)
150
+ img_placeholder += '<Img><ImageHere>'
151
+
152
+ if subtitle_text_in_interval != "" and number_of_words < max_sub_len:
153
+ img_placeholder += f'<Cap>{subtitle_text_in_interval}'
154
+ number_of_words += len(subtitle_text_in_interval.split(' '))
155
+ subtitle_text_in_interval = ""
156
+
157
+ frame_count += 1
158
+ if len(images) >= max_frames:
159
+ break
160
+
161
+ cap.release()
162
+ cv2.destroyAllWindows()
163
+
164
+ if len(images) == 0:
165
+ return None, None
166
+
167
+ images = torch.stack(images)
168
+ return images, img_placeholder
169
+
170
+ def extract_audio(video_path, audio_path):
171
+ """提取音频"""
172
+ video_clip = mp.VideoFileClip(video_path)
173
+ audio_clip = video_clip.audio
174
+ audio_clip.write_audiofile(audio_path, codec="libmp3lame", bitrate="320k", verbose=False, logger=None)
175
+ video_clip.close()
176
+
177
+ def get_subtitles(video_path):
178
+ """生成字幕"""
179
+ global whisper_model
180
+
181
+ if whisper_model is None:
182
+ return None
183
+
184
+ audio_dir = "workspace/inference_subtitles/mp3"
185
+ subtitle_dir = "workspace/inference_subtitles"
186
+ os.makedirs(subtitle_dir, exist_ok=True)
187
+ os.makedirs(audio_dir, exist_ok=True)
188
+
189
+ video_id = video_path.split('/')[-1].split('.')[0]
190
+ audio_path = f"{audio_dir}/{video_id}.mp3"
191
+ subtitle_path = f"{subtitle_dir}/{video_id}.vtt"
192
+
193
+ # 如果字幕已存在,直接返回
194
+ if os.path.exists(subtitle_path):
195
+ return subtitle_path
196
+
197
+ try:
198
+ extract_audio(video_path, audio_path)
199
+ result = whisper_model.transcribe(audio_path, language="en")
200
+
201
+ # 创建VTT文件
202
+ with open(subtitle_path, "w", encoding="utf-8") as vtt_file:
203
+ vtt_file.write("WEBVTT\n\n")
204
+ for segment in result['segments']:
205
+ start = format_timestamp(segment['start'])
206
+ end = format_timestamp(segment['end'])
207
+ text = segment['text']
208
+ vtt_file.write(f"{start} --> {end}\n{text}\n\n")
209
+
210
+ return subtitle_path
211
+ except Exception as e:
212
+ print(f"字幕生成错误: {e}")
213
+ return None
214
 
215
+ def prepare_input(video_path, subtitle_path, instruction):
216
+ """准备输入"""
217
+ global args
218
+
219
+ # 根据模型设置参数
220
+ if args and "mistral" in args.ckpt:
221
+ max_frames = 90
222
+ max_sub_len = 800
223
+ else:
224
+ max_frames = 45
225
+ max_sub_len = 400
226
+
227
+ sampling_interval, fps = extract_video_info(video_path, max_frames)
228
+ subtitles = extract_subtitles(subtitle_path)
229
+ frames_features, input_placeholder = match_frames_and_subtitles(
230
+ video_path, subtitles, sampling_interval, max_sub_len, fps, max_frames
231
+ )
232
+
233
+ if input_placeholder:
234
+ input_placeholder += "\n" + instruction
235
+ else:
236
+ input_placeholder = instruction
237
+
238
+ return frames_features, input_placeholder
239
+
240
+ def model_generate(*model_args, **kwargs):
241
+ """模型生成函数"""
242
+ global model
243
+
244
+ with model.maybe_autocast():
245
+ output = model.llama_model.generate(*model_args, **kwargs)
246
+ return output
247
+
248
+ def generate_prediction(video_path, instruction, gen_subtitles=True, stream=False):
249
+ """生成预测结果"""
250
+ global model, args, seed
251
+
252
+ if gen_subtitles:
253
+ subtitle_path = get_subtitles(video_path)
254
+ else:
255
+ subtitle_path = None
256
+
257
+ prepared_images, prepared_instruction = prepare_input(video_path, subtitle_path, instruction)
258
+
259
+ if prepared_images is None:
260
+ return "视频无法打开,请检查视频路径"
261
+
262
+ length = len(prepared_images)
263
+ prepared_images = prepared_images.unsqueeze(0)
264
+
265
+ conv = CONV_VISION.copy()
266
+ conv.system = ""
267
+ conv.append_message(conv.roles[0], prepared_instruction)
268
+ conv.append_message(conv.roles[1], None)
269
+ prompt = [conv.get_prompt()]
270
+
271
+ # 设置随机种子
272
+ setup_seeds(seed)
273
+
274
+ try:
275
+ answers = model.generate(
276
+ prepared_images,
277
+ prompt,
278
+ max_new_tokens=args.max_new_tokens if args else 512,
279
+ do_sample=True,
280
+ lengths=[length],
281
+ num_beams=1
282
+ )
283
+ return answers[0]
284
+ except Exception as e:
285
+ return f"生成预测时出错: {str(e)}"
286
+
287
+ # ======================== 巨量引擎规则检测函数 ========================
288
+
289
+ def format_violations_report(violations_result):
290
+ """格式化违规检测报告"""
291
+ if not violations_result["has_violations"]:
292
+ return """
293
+ 🛡️ **巨量引擎规则检测结果**: ✅ 无违规内容
294
+ - 已检测规则: 299条巨量引擎禁投规则
295
+ - 检测维度: 低危(P1) + 中危(P2) + 高危(P3)
296
+ - 检测结果: 内容符合平台规范
297
+ """
298
+
299
+ report = f"""
300
+ 🚨 **巨量引擎规则检测结果**: ⚠️ 发现 {violations_result["total_violations"]} 项违规
301
+
302
+ 📊 **违规统计**:
303
+ - 🔴 高危违规(P3): {violations_result["high_risk"]["count"]} 项
304
+ - 🟡 中危违规(P2): {violations_result["medium_risk"]["count"]} 项
305
+ - 🟠 低危违规(P1): {violations_result["low_risk"]["count"]} 项
306
+
307
+ 📋 **详细违规列表**:
308
+ """
309
+
310
+ # 按风险等级排序显���违规
311
+ for violation in sorted(violations_result["all_violations"],
312
+ key=lambda x: {"P3": 3, "P2": 2, "P1": 1}[x["risk_level"]],
313
+ reverse=True):
314
+ risk_icon = {"P3": "🚨", "P2": "⚠️", "P1": "💭"}[violation["risk_level"]]
315
+ report += f"""
316
+ {risk_icon} **{violation["risk_level"]} - {violation["category"]}**
317
+ 规则: {violation["description"]}
318
+ 匹配词: "{violation["matched_keyword"]}"
319
+ 规则ID: {violation["rule_id"]}
320
+ """
321
+
322
+ return report
323
+
324
+ def get_overall_risk_level(violations_result):
325
+ """获取综合风险等级"""
326
+ if not violations_result["has_violations"]:
327
+ return "✅ P3 (安全) - 内容健康,符合平台规范"
328
+
329
+ if violations_result["high_risk"]["count"] > 0:
330
+ return f"🚨 P0 (极高危) - 发现 {violations_result['high_risk']['count']} 项高危违规,禁止投放"
331
+ elif violations_result["medium_risk"]["count"] > 2:
332
+ return f"⚠️ P1 (高危) - 发现 {violations_result['medium_risk']['count']} 项中危违规,需严格审核"
333
+ elif violations_result["medium_risk"]["count"] > 0:
334
+ return f"⚠️ P1 (中危) - 发现 {violations_result['medium_risk']['count']} 项中危违规,需要审核"
335
+ else:
336
+ return f"⚡ P2 (低危) - 发现 {violations_result['low_risk']['count']} 项低危违规,建议关注"
337
+
338
+ # ======================== 应用主要函数 ========================
339
+
340
+ def setup_seeds(seed):
341
+ """设置随机种子"""
342
+ random.seed(seed)
343
+ np.random.seed(seed)
344
+ torch.manual_seed(seed)
345
+ torch.cuda.manual_seed(seed)
346
+ cudnn.benchmark = False
347
+ cudnn.deterministic = True
348
+
349
+ def optimize_gpu_memory():
350
+ """GPU内存优化"""
351
+ print("🔍 开始GPU内存优化...")
352
+
353
+ # 设置环境变量优化内存分配
354
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:256,garbage_collection_threshold:0.6'
355
+ os.environ['CUDA_LAUNCH_BLOCKING'] = '1'
356
+
357
+ if torch.cuda.is_available():
358
+ print(f"🔍 GPU: {torch.cuda.get_device_name(0)}")
359
+ print(f"💾 总显存: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.1f} GB")
360
+
361
+ # 强制清理所有GPU缓存
362
+ torch.cuda.empty_cache()
363
+ torch.cuda.ipc_collect()
364
+ gc.collect()
365
+
366
+ # 设置内存增长策略
367
+ torch.backends.cudnn.benchmark = False
368
+ torch.backends.cudnn.deterministic = True
369
+
370
+ print(f"💾 清理后可用显存: {(torch.cuda.get_device_properties(0).total_memory - torch.cuda.memory_allocated(0)) / 1024**3:.1f} GB")
371
+
372
+ def get_arguments():
373
+ """获取参数配置"""
374
+ parser = argparse.ArgumentParser(description="MiniGPT4-Video参数")
375
+ parser.add_argument("--cfg-path", help="配置文件路径",
376
+ default="test_configs/minigpt4_optimized_config.yaml")
377
+ parser.add_argument("--ckpt", type=str,
378
+ default='checkpoints/video_llama_checkpoint_last.pth',
379
+ help="模型检查点路径")
380
+ parser.add_argument("--max_new_tokens", type=int, default=512,
381
+ help="最大生成token数")
382
+ parser.add_argument("--lora_r", type=int, default=96, help="LoRA rank")
383
+ parser.add_argument("--lora_alpha", type=int, default=24, help="LoRA alpha")
384
+ parser.add_argument("--options", nargs="+", help="覆盖配置选项")
385
+ return parser.parse_args()
386
+
387
+ def load_minigpt4_model():
388
+ """加载MiniGPT4-Video模型"""
389
+ global model, vis_processor, whisper_model, args, seed
390
 
391
  if model is not None:
392
+ return model, vis_processor, whisper_model
393
 
394
  try:
395
+ print("🔄 正在加载MiniGPT4-Video模型...")
396
+
397
+ # 获取参数
398
+ args = get_arguments()
399
+
400
+ # 加载配置
401
+ config_path = args.cfg_path
402
+ if not os.path.exists(config_path):
403
+ config_path = "test_configs/llama2_test_config.yaml" # 回退到默认配置
404
+
405
+ with open(config_path) as file:
406
+ config = yaml.load(file, Loader=yaml.FullLoader)
407
+
408
+ seed = config['run']['seed']
409
+ setup_seeds(seed)
410
 
411
+ # GPU内存优化
412
+ optimize_gpu_memory()
413
 
414
+ print("🚀 开始初始化MiniGPT4-Video模型...")
415
+ model, vis_processor, whisper_gpu_id, minigpt4_gpu_id, answer_module_gpu_id = init_model(args)
416
+
417
+ # 清理缓存
418
+ if torch.cuda.is_available():
419
+ torch.cuda.empty_cache()
420
+ print(f"💾 模型加载后显存使用: {torch.cuda.memory_allocated(0) / 1024**3:.1f} GB")
421
+
422
+ print("🚀 开始初始化Whisper模型...")
423
+ whisper_model = whisper.load_model("base").to(f"cuda:{whisper_gpu_id}" if torch.cuda.is_available() else "cpu")
424
+
425
+ if torch.cuda.is_available():
426
+ print(f"💾 全部加载后显存使用: {torch.cuda.memory_allocated(0) / 1024**3:.1f} GB")
427
+
428
+ print("✅ 所有模型加载完成!")
429
+ return model, vis_processor, whisper_model
430
 
431
  except Exception as e:
432
  print(f"❌ 模型加载失败: {e}")
433
+ print("🔄 回退到模拟模式...")
434
+ return None, None, None
435
 
436
  @spaces.GPU if GPU_AVAILABLE else lambda f: f
437
+ def analyze_video_with_minigpt4(video_file, instruction):
438
+ """使用MiniGPT4-Video分析视频内容并进行巨量引擎规则检测"""
439
+ if video_file is None:
440
+ return "❌ 请上传视频文件", "无法评估"
441
+
442
  try:
443
  # 加载模型
444
+ model_loaded, vis_proc, whisper_loaded = load_minigpt4_model()
 
 
445
 
446
+ if model_loaded is None:
447
+ # 模拟模式
448
+ return f"""
449
+ 🎬 **视频内容分析结果 (模拟模式)**
450
+
451
+ 📋 **基本信息**:
452
+ - 视频文件: {video_file}
453
+ - 分析指令: {instruction}
454
+
455
+ ⚠️ **注意**: 当前运行在模拟模式,真实模型加载失败
456
+ 请检查模型文件和配置是否正确
457
+
458
+ 🛡️ **巨量引擎规则检测**: 仅在真实模式下可用
459
+ """, "⚠️ 模拟模式"
460
+
461
+ print(f"🔄 开始分析视频: {video_file}")
462
  print(f"📝 分析指令: {instruction}")
463
 
464
+ # 复制视频到临时路径(如果需要)
465
+ temp_video_path = video_file
466
+ if not os.path.exists(video_file):
467
+ # 如果是Gradio的临时文件,复制到工作目录
468
+ temp_dir = "workspace/tmp"
469
+ os.makedirs(temp_dir, exist_ok=True)
470
+ temp_video_path = os.path.join(temp_dir, "analysis_video.mp4")
471
+ shutil.copy2(video_file, temp_video_path)
472
+
473
+ # 使用MiniGPT4-Video进行真实分析
474
+ if not instruction or instruction.strip() == "":
475
+ instruction = "请详细分析这个视频的内容,包括场景、人物、动作、对话等,并描述所有可见和可听的元素。"
476
+
477
+ # 调用MiniGPT4-Video的生成函数
478
+ prediction = generate_prediction(
479
+ video_path=temp_video_path,
480
+ instruction=instruction,
481
+ gen_subtitles=True, # 生成字幕
482
+ stream=False
483
+ )
484
+
485
+ # 🚨 巨量引擎规则检测 🚨
486
+ print("🔍 开始巨量引擎299条规则检测...")
487
+ violations_result = rules_engine.check_all_content(prediction, instruction)
488
+
489
+ # 格式化完整分析报告
490
+ enhanced_result = f"""
491
+ 🎬 **MiniGPT4-Video 视频内容分析 + 巨量引擎规则检测报告**
492
 
493
+ 📋 **基本信息**:
494
+ - 视频文件: {os.path.basename(video_file)}
495
+ - 分析设备: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU模式'}
496
  - 分析指令: {instruction}
497
 
498
+ 🔍 **视频内容描述**:
499
+ {prediction}
 
 
500
 
501
+ {format_violations_report(violations_result)}
 
 
 
502
 
503
+ 📊 **技术信息**:
504
+ - 内容理解: MiniGPT4-Video + Whisper
505
+ - 规则引擎: 巨量引擎299条禁投规则
506
+ - 检测等级: P1(低危) + P2(中危) + P3(高危)
507
+ - 分析模式: 多模态理解 (视觉+语音+文本)
508
+
509
+ 💡 **说明**:
510
+ 基于MiniGPT4-Video的深度内容理解,结合巨量引擎完整禁投规则库进行专业违规检测。
511
  """
512
 
513
+ # 获取综合风险等级
514
+ safety_score = get_overall_risk_level(violations_result)
515
 
516
+ return enhanced_result, safety_score
517
 
518
  except Exception as e:
519
+ error_msg = f"""
520
+ ❌ **分析过程中出错**
521
+
522
+ 错误信息: {str(e)}
523
+
524
+ 🔄 **可能的解决方案**:
525
+ 1. 检查视频文件格式 (建议MP4)
526
+ 2. 确认模型文件是否正确加载
527
+ 3. 检查GPU内存是否充足
528
+ 4. 验证配置文件路径
529
+
530
+ 💡 **提示**: 如果问题持续,请检查模型和依赖项安装
531
+ """
532
  return error_msg, "⚠️ 错误"
533
 
534
+ def create_app():
535
+ """��建Gradio应用"""
536
 
537
+ interface = gr.Interface(
538
+ fn=analyze_video_with_minigpt4,
539
+ inputs=[
540
+ gr.Video(label="上传视频文件"),
541
+ gr.Textbox(
542
+ label="分析指令",
543
+ value="请详细分析这个视频的内容,包括场景、人物、动作、对话等,并描述所有可见和可听的元素。",
544
+ placeholder="输入您希望AI如何分析这个视频...",
545
+ lines=3
546
+ )
547
+ ],
548
+ outputs=[
549
+ gr.Textbox(label="MiniGPT4-Video 内容分析 + 巨量引擎规则检测", lines=20),
550
+ gr.Textbox(label="巨量引擎风险评级")
551
+ ],
552
+ title="🎥 智能视频内容安全分析 - MiniGPT4-Video + 巨量引擎",
553
+ description="""
554
+ ## 🎬 基于MiniGPT4-Video + 巨量引擎299条禁投规则的专业视频安全检测系统
555
 
556
+ **ZeroGPU加速** | 🎬 **MiniGPT4-Video** | 🎙️ **Whisper语音** | 🛡️ **巨量引擎299条规则**
 
557
 
558
+ **🔥 核心功能:**
559
+ - 🎞️ **深度视频理解**: MiniGPT4-Video多模态分析
560
+ - 🎙️ **语音转文字**: Whisper自动生成字幕
561
+ - 🛡️ **专业违规检测**: 巨量引擎完整禁投规则库
562
+ - 📊 **智能风险评级**: P0-P3四级风险等级
563
+
564
+ **🎯 检测维度:**
565
+ - **高危(P3)**: 违法出版物、烟草、医疗等严重违规
566
+ - **中危(P2)**: 赌博周边、房地产、金融等中等风险
567
+ - **低危(P1)**: 化妆品、汽车、游戏等轻微风险
568
+
569
+ **📋 规则覆盖:**
570
+ 涵盖化妆品类、汽车类、游戏类、赌博类、房地产类、工具软件类、教育培训类、
571
+ 金融类、医疗类、烟草类等全部299条巨量引擎禁投规则
572
+ """,
573
+ examples=[
574
+ [None, "分析这个视频是否包含禁投内容"],
575
+ [None, "检测视频中是否有巨量引擎禁止的产品或服务"],
576
+ [None, "评估视频内容的投放风险等级"],
577
+ [None, "详细描述视频内容并进行合规检查"]
578
+ ],
579
+ cache_examples=False
580
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
 
582
+ return interface
583
 
584
  def main():
585
  """主函数"""
586
+ print("🚀 启动MiniGPT4-Video + 巨量引擎视频安全分析应用")
587
+ print("🎬 MiniGPT4-Video: 深度视频内容理解")
588
+ print("🛡️ 巨量引擎: 299条禁投规则检测")
589
 
 
590
  if torch.cuda.is_available():
591
  print(f"✅ GPU可用: {torch.cuda.get_device_name(0)}")
592
  else:
593
  print("⚠️ 使用CPU模式")
594
 
595
+ # 创建必要的目录
596
+ os.makedirs("workspace/tmp", exist_ok=True)
597
+ os.makedirs("workspace/inference_subtitles", exist_ok=True)
598
+ os.makedirs("workspace/inference_subtitles/mp3", exist_ok=True)
599
+
600
+ print("📁 工作目录准备完成")
601
+ print("🚀 正在启动Gradio应用...")
602
 
603
+ app = create_app()
604
+
605
+ # 启动应用
606
  app.launch(
607
+ share=True,
608
  server_name="0.0.0.0",
609
  server_port=7860,
 
610
  show_error=True
611
  )
612