SOONet效果可视化:输出JSON结果转SRT字幕+FFmpeg自动打点生成剪辑工程

张开发
2026/6/12 12:31:39 15 分钟阅读
SOONet效果可视化:输出JSON结果转SRT字幕+FFmpeg自动打点生成剪辑工程
SOONet效果可视化输出JSON结果转SRT字幕FFmpeg自动打点生成剪辑工程你是不是也遇到过这样的问题用AI模型分析了一段长视频找到了关键片段结果拿到手的是一堆冷冰冰的JSON数据。看着那些时间戳和分数还得手动去视频里找对应的片段费时费力不说还容易出错。今天我就来分享一个实用的解决方案把SOONet输出的JSON结果自动转换成SRT字幕文件再用FFmpeg一键生成带标记点的剪辑工程。这样你不仅能直观地看到模型定位的片段还能直接导入剪辑软件开始工作效率提升不止一点点。1. 为什么需要可视化SOONet的结果SOONet确实很强大一次推理就能在小时级的长视频里找到你想要的片段。但它的输出是这样的{ scores: [0.85, 0.72, 0.68], timestamps: [ [12.5, 18.3], [45.2, 52.1], [78.9, 84.7] ] }对于大多数人来说这串数字意味着什么你得打开视频播放器手动跳到12.5秒看看是不是你要的内容再跳到45.2秒...来回对比确认这个过程太麻烦了特别是当你有几十个片段需要处理时手动操作简直就是噩梦。可视化能帮你解决三个核心问题直观验证一眼就能看到模型找得准不准快速定位点击时间点直接跳转到对应视频位置批量处理一次性处理所有片段不用一个个手动找2. 从JSON到SRT让时间戳变成可点击的字幕SRT是最常见的字幕格式几乎所有的视频播放器都支持。它的好处是你点击字幕行播放器会自动跳转到对应的时间点。2.1 理解SRT格式先来看一个SRT文件长什么样1 00:00:12,500 -- 00:00:18,300 [片段1] 置信度: 85% 2 00:00:45,200 -- 00:00:52,100 [片段2] 置信度: 72% 3 00:01:18,900 -- 00:01:24,700 [片段3] 置信度: 68%每段字幕包含三部分序号从1开始递增时间范围开始时间 -- 结束时间格式时:分:秒,毫秒字幕内容可以自定义显示的信息2.2 Python转换脚本下面这个脚本能把SOONet的JSON结果自动转换成SRT文件import json from datetime import timedelta def json_to_srt(json_data, output_fileoutput.srt, query_text): 将SOONet的JSON结果转换为SRT字幕文件 参数 json_data: SOONet的输出结果字典或JSON字符串 output_file: 输出的SRT文件路径 query_text: 查询文本会显示在字幕中 # 如果输入是字符串先解析为字典 if isinstance(json_data, str): data json.loads(json_data) else: data json_data # 获取时间戳和分数 timestamps data.get(timestamps, []) scores data.get(scores, []) # 确保数据长度一致 if len(timestamps) ! len(scores): print(警告时间戳和分数数量不一致) min_len min(len(timestamps), len(scores)) timestamps timestamps[:min_len] scores scores[:min_len] srt_content [] for i, (start_end, score) in enumerate(zip(timestamps, scores), 1): start_time, end_time start_end # 将秒转换为SRT时间格式时:分:秒,毫秒 def seconds_to_srt_time(seconds): td timedelta(secondsseconds) hours td.seconds // 3600 minutes (td.seconds % 3600) // 60 seconds td.seconds % 60 milliseconds int(td.microseconds / 1000) return f{hours:02d}:{minutes:02d}:{seconds:02d},{milliseconds:03d} start_srt seconds_to_srt_time(start_time) end_srt seconds_to_srt_time(end_time) # 生成字幕内容 score_percent int(score * 100) subtitle_text f[片段{i}] if query_text: subtitle_text f 查询: {query_text} subtitle_text f 置信度: {score_percent}% # 构建SRT段落 srt_block f{i}\n{start_srt} -- {end_srt}\n{subtitle_text}\n srt_content.append(srt_block) # 写入文件 with open(output_file, w, encodingutf-8) as f: f.write(\n.join(srt_content)) print(fSRT文件已生成: {output_file}) print(f共转换了 {len(srt_content)} 个片段) return output_file # 使用示例 if __name__ __main__: # SOONet的输出结果 soonet_result { scores: [0.85, 0.72, 0.68], timestamps: [ [12.5, 18.3], [45.2, 52.1], [78.9, 84.7] ] } # 转换为SRT srt_file json_to_srt( soonet_result, output_filevideo_segments.srt, query_texta man takes food out of the refrigerator )2.3 如何使用生成的SRT文件生成SRT文件后你可以用播放器直接查看把SRT文件和视频放在同一个文件夹且文件名相同如video.mp4和video.srt用VLC、PotPlayer等播放器打开视频字幕会自动加载点击字幕行视频会自动跳转到对应时间点用剪辑软件导入在Premiere、Final Cut Pro、DaVinci Resolve中导入SRT作为字幕轨道字幕会显示在时间轴上方便你快速定位到每个片段进一步处理可以修改SRT文件添加更多信息可以合并多个查询结果可以筛选高置信度的片段3. 用FFmpeg自动打点生成剪辑标记文件SRT文件能帮你快速定位但如果要做剪辑还需要更专业的工具。FFmpeg可以生成剪辑软件能识别的标记点文件。3.1 生成EDL编辑决策列表EDLEdit Decision List是剪辑行业的标准格式很多软件都支持。import json def json_to_edl(json_data, output_fileoutput.edl, video_fileinput.mp4): 将SOONet结果转换为EDL编辑决策列表 EDL格式 TITLE: 标题 FCM: 帧率模式 001 AX V C 00:00:12:00 00:00:18:00 00:00:00:00 00:00:06:00 | | | | | | | | | | | | | | | 出点时间线 | | | | | | 入点时间线 | | | | | 出点源素材 | | | | 入点源素材 | | | 轨道V视频A音频 | | 轨道类型AX视频轨道 | 源素材名称 事件编号 if isinstance(json_data, str): data json.loads(json_data) else: data json_data timestamps data.get(timestamps, []) scores data.get(scores, []) # EDL头部 edl_content [ TITLE: SOONet Auto-generated EDL, FCM: NON-DROP FRAME, ] for i, (start_end, score) in enumerate(zip(timestamps, scores), 1): start_time, end_time start_end duration end_time - start_time # 转换为EDL时间格式时:分:秒:帧 # 假设帧率为25fps fps 25 def seconds_to_edl_time(seconds): hours int(seconds // 3600) minutes int((seconds % 3600) // 60) secs int(seconds % 60) frames int((seconds - int(seconds)) * fps) return f{hours:02d}:{minutes:02d}:{secs:02d}:{frames:02d} source_in seconds_to_edl_time(start_time) source_out seconds_to_edl_time(end_time) # 时间线上的入点和出点从0开始连续 timeline_in seconds_to_edl_time(sum([ts[1]-ts[0] for ts in timestamps[:i-1]])) timeline_out seconds_to_edl_time(sum([ts[1]-ts[0] for ts in timestamps[:i]])) # 构建EDL行 edl_line f{i:03d} {video_file} V C {source_in} {source_out} {timeline_in} {timeline_out} edl_content.append(edl_line) # 写入文件 with open(output_file, w, encodingutf-8) as f: f.write(\n.join(edl_content)) print(fEDL文件已生成: {output_file}) return output_file # 使用示例 edl_file json_to_edl( soonet_result, output_filevideo_edit.edl, video_filemy_video.mp4 )3.2 生成XML时间线文件兼容Premiere对于Adobe Premiere用户XML是更好的选择import json from datetime import timedelta def json_to_premiere_xml(json_data, output_fileoutput.xml, video_fileinput.mp4): 生成Premiere能识别的XML时间线文件 if isinstance(json_data, str): data json.loads(json_data) else: data json_data timestamps data.get(timestamps, []) # XML头部 xml_content [ ?xml version1.0 encodingUTF-8?, !DOCTYPE xmeml, xmeml version5, sequence, nameSOONet Auto-Edit/name, duration0/duration, rate, timebase25/timebase, ntscFALSE/ntsc, /rate, media, video, track ] # 添加每个片段 for i, (start_time, end_time) in enumerate(timestamps, 1): duration end_time - start_time clip_entry [ f clipitem idclipitem-{i}, f nameSegment {i}/name, f duration{int(duration * 25)}/duration, rate, timebase25/timebase, ntscFALSE/ntsc, /rate, start0/start, end0/end, in0/in, f out{int(duration * 25)}/out, file idfile-1, f name{video_file}/name, pathurlfile://localhost/path/to/your/video.mp4/pathurl, /file, f sourcestart{int(start_time * 25)}/sourcestart, f sourceend{int(end_time * 25)}/sourceend, /clipitem ] xml_content.extend(clip_entry) # XML尾部 xml_content.extend([ /track, /video, /media, /sequence, /xmeml ]) # 写入文件 with open(output_file, w, encodingutf-8) as f: f.write(\n.join(xml_content)) print(fPremiere XML文件已生成: {output_file}) return output_file3.3 一键生成剪辑标记脚本最方便的方式是写一个Bash脚本把所有功能整合在一起#!/bin/bash # soonet_auto_edit.sh - SOONet结果自动处理脚本 # 配置参数 INPUT_JSONsoonet_result.json VIDEO_FILEinput_video.mp4 QUERY_TEXTyour query text here OUTPUT_PREFIXoutput echo SOONet结果自动处理脚本 echo 输入JSON: $INPUT_JSON echo 视频文件: $VIDEO_FILE echo 查询文本: $QUERY_TEXT echo # 1. 生成SRT字幕文件 echo 1. 生成SRT字幕文件... python3 -c import json with open($INPUT_JSON, r) as f: data json.load(f) timestamps data.get(timestamps, []) scores data.get(scores, []) with open(${OUTPUT_PREFIX}.srt, w, encodingutf-8) as f: for i, ((start, end), score) in enumerate(zip(timestamps, scores), 1): # 转换时间格式 def to_srt_time(seconds): hours int(seconds // 3600) minutes int((seconds % 3600) // 60) secs int(seconds % 60) millis int((seconds - int(seconds)) * 1000) return f{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d} start_time to_srt_time(start) end_time to_srt_time(end) score_percent int(score * 100) f.write(f{i}\\n) f.write(f{start_time} -- {end_time}\\n) f.write(f[片段{i}] 置信度: {score_percent}%\\n) f.write(\\n) print(SRT文件生成完成: ${OUTPUT_PREFIX}.srt) # 2. 用FFmpeg生成带标记点的视频 echo echo 2. 生成带标记点的视频预览... # 先提取音频用于预览 ffmpeg -i $VIDEO_FILE -q:a 0 -map a ${OUTPUT_PREFIX}_audio.mp3 -y 2/dev/null # 生成标记点信息文件 python3 -c import json with open($INPUT_JSON, r) as f: data json.load(f) timestamps data.get(timestamps, []) with open(markers.txt, w) as f: for i, (start, end) in enumerate(timestamps, 1): f.write(fbetween(t,{start},{end})*0.9between(t,{start-0.5},{start})*(t-{start-0.5})*0.9/0.5between(t,{end},{end0.5})*({end0.5}-t)*0.9/0.5\\n) # 用FFmpeg生成带波形和标记点的预览视频 ffmpeg -i $VIDEO_FILE -i ${OUTPUT_PREFIX}_audio.mp3 \ -filter_complex [0:v]scale1280:720[video]; [1:a]showwavess1280x200:modeline:colors0x00ff00:scalesqrt[waves]; [video][waves]vstackinputs2[output]; [output]drawtexttextSOONet标记点预览:x10:y10:fontsize24:fontcolorwhite:box1:boxcolor0x0000000.5, drawtexttext查询: $QUERY_TEXT:x10:y50:fontsize20:fontcoloryellow:box1:boxcolor0x0000000.5 \ -c:v libx264 -preset fast -crf 23 \ -c:a aac -b:a 128k \ ${OUTPUT_PREFIX}_preview.mp4 -y 2/dev/null echo 预览视频生成完成: ${OUTPUT_PREFIX}_preview.mp4 # 3. 生成剪辑软件兼容的文件 echo echo 3. 生成剪辑工程文件... # 生成简单的剪辑列表 python3 -c import json from datetime import timedelta with open($INPUT_JSON, r) as f: data json.load(f) timestamps data.get(timestamps, []) scores data.get(scores, []) # 生成剪辑列表 with open(${OUTPUT_PREFIX}_edit_list.csv, w, encodingutf-8) as f: f.write(序号,开始时间(秒),结束时间(秒),时长(秒),置信度,描述\\n) total_duration 0 for i, ((start, end), score) in enumerate(zip(timestamps, scores), 1): duration end - start score_percent int(score * 100) # 转换为时分秒格式 def format_time(seconds): td timedelta(secondsseconds) hours td.seconds // 3600 minutes (td.seconds % 3600) // 60 secs td.seconds % 60 return f{hours:02d}:{minutes:02d}:{secs:02d} start_formatted format_time(start) end_formatted format_time(end) f.write(f{i},{start:.2f},{end:.2f},{duration:.2f},{score_percent}%,片段{i}\\n) total_duration duration print(f总剪辑时长: {total_duration:.2f}秒) print(f片段数量: {len(timestamps)}) print(剪辑列表生成完成: ${OUTPUT_PREFIX}_edit_list.csv) echo echo 处理完成 echo 生成的文件: echo 1. ${OUTPUT_PREFIX}.srt - SRT字幕文件 echo 2. ${OUTPUT_PREFIX}_preview.mp4 - 带标记点的预览视频 echo 3. ${OUTPUT_PREFIX}_edit_list.csv - 剪辑列表 echo echo 使用方法: echo - 用播放器打开 .srt 文件查看字幕标记 echo - 用剪辑软件导入 .csv 文件开始剪辑 echo - 查看 .mp4 文件预览标记位置4. 实际应用案例从分析到剪辑的全流程让我用一个真实案例来展示整个流程。假设你是一个视频编辑需要从2小时的会议录像中找出所有讨论产品功能的片段。4.1 第一步用SOONet分析视频# 使用SOONet分析视频 from modelscope.pipelines import pipeline from modelscope.utils.constant import Tasks # 初始化SOONet soonet pipeline( Tasks.video_temporal_grounding, modelpath/to/soonet/model ) # 分析视频 query discussing product features and specifications video_path meeting_recording.mp4 result soonet((query, video_path)) # 保存结果 import json with open(meeting_analysis.json, w) as f: json.dump(result, f, indent2) print(f找到 {len(result[timestamps])} 个相关片段)4.2 第二步自动生成可视化文件运行我们的处理脚本# 修改脚本中的参数 INPUT_JSONmeeting_analysis.json VIDEO_FILEmeeting_recording.mp4 QUERY_TEXTdiscussing product features OUTPUT_PREFIXproduct_features # 运行脚本 bash soonet_auto_edit.sh4.3 第三步在剪辑软件中使用生成的三个文件各有用途product_features.srt用VLC播放器打开视频自动加载字幕点击字幕行直接跳转到对应片段快速验证SOONet找得准不准product_features_preview.mp4视频上方有波形图绿色高亮显示相关片段快速浏览所有标记点分享给团队成员确认product_features_edit_list.csv导入到Premiere或Final Cut Pro自动创建时间线标记开始剪辑工作4.4 第四步批量处理多个查询如果你有多个查询需求可以批量处理# 批量处理多个查询 queries [ discussing product features, showing demo of the product, customer feedback session, technical specifications discussion ] all_segments [] for query in queries: print(f处理查询: {query}) result soonet((query, video_path)) # 为每个片段添加查询标签 for (start, end), score in zip(result[timestamps], result[scores]): all_segments.append({ start: start, end: end, score: score, query: query, duration: end - start }) # 按时间排序 all_segments.sort(keylambda x: x[start]) # 合并重叠的片段 merged_segments [] current None for seg in all_segments: if current is None: current seg.copy() elif seg[start] current[end]: # 重叠合并 current[end] max(current[end], seg[end]) current[duration] current[end] - current[start] current[query] f{current[query]}, {seg[query]} else: # 不重叠保存当前开始新的 merged_segments.append(current) current seg.copy() if current: merged_segments.append(current) print(f原始片段: {len(all_segments)} 个) print(f合并后: {len(merged_segments)} 个) print(f总时长: {sum(s[duration] for s in merged_segments):.1f} 秒) # 保存合并后的结果 with open(merged_analysis.json, w) as f: json.dump({ timestamps: [[s[start], s[end]] for s in merged_segments], scores: [s[score] for s in merged_segments], queries: [s[query] for s in merged_segments] }, f, indent2)5. 高级技巧优化可视化效果5.1 添加置信度颜色编码在SRT文件中用不同颜色表示置信度def add_color_to_srt(srt_file, json_data): 在SRT中添加颜色标记 if isinstance(json_data, str): data json.loads(json_data) else: data json_data scores data.get(scores, []) with open(srt_file, r, encodingutf-8) as f: lines f.readlines() # 修改SRT内容添加颜色 colored_lines [] segment_idx 0 for i in range(0, len(lines), 4): if i 3 len(lines): # 获取置信度 if segment_idx len(scores): score scores[segment_idx] # 根据置信度选择颜色 if score 0.8: color 00FF00 # 绿色高置信度 elif score 0.6: color FFFF00 # 黄色中置信度 else: color FF0000 # 红色低置信度 # 添加颜色标签部分播放器支持 content_line lines[i2].strip() colored_content ffont color\{color}\{content_line}/font lines[i2] colored_content \n segment_idx 1 # 保存带颜色的版本 colored_file srt_file.replace(.srt, _colored.srt) with open(colored_file, w, encodingutf-8) as f: f.writelines(lines) return colored_file5.2 生成交互式HTML报告创建一个可以在浏览器中查看的交互式报告def generate_html_report(json_data, video_file, output_filereport.html): 生成交互式HTML报告 if isinstance(json_data, str): data json.loads(json_data) else: data json_data timestamps data.get(timestamps, []) scores data.get(scores, []) html_content f !DOCTYPE html html head titleSOONet分析报告 - {video_file}/title style body {{ font-family: Arial, sans-serif; margin: 20px; }} .segment {{ margin: 10px 0; padding: 10px; border-left: 5px solid #4CAF50; background-color: #f9f9f9; }} .high-confidence {{ border-left-color: #4CAF50; }} .medium-confidence {{ border-left-color: #FFC107; }} .low-confidence {{ border-left-color: #F44336; }} .time {{ color: #666; font-size: 0.9em; }} .confidence {{ display: inline-block; padding: 2px 8px; border-radius: 3px; color: white; font-size: 0.8em; }} .high {{ background-color: #4CAF50; }} .medium {{ background-color: #FFC107; }} .low {{ background-color: #F44336; }} button {{ background-color: #008CBA; color: white; border: none; padding: 5px 10px; cursor: pointer; margin-right: 5px; }} button:hover {{ background-color: #005f73; }} /style /head body h1SOONet视频分析报告/h1 p视频文件: strong{video_file}/strong/p p找到 strong{len(timestamps)}/strong 个相关片段/p div idsegments for i, ((start, end), score) in enumerate(zip(timestamps, scores), 1): # 计算时长 duration end - start # 确定置信度等级 if score 0.8: conf_class high seg_class high-confidence elif score 0.6: conf_class medium seg_class medium-confidence else: conf_class low seg_class low-confidence # 格式化时间 def format_time(seconds): hours int(seconds // 3600) minutes int((seconds % 3600) // 60) secs int(seconds % 60) return f{hours:02d}:{minutes:02d}:{secs:02d} start_formatted format_time(start) end_formatted format_time(end) html_content f div classsegment {seg_class} h3片段 {i}/h3 p classtime时间: {start_formatted} - {end_formatted} (时长: {duration:.1f}秒)/p p置信度: span classconfidence {conf_class}{score*100:.1f}%/span/p div button onclickplaySegment({start})播放此片段/button button onclickcopyTimecode({start}, {end})复制时间码/button /div /div html_content /div script function playSegment(startTime) { // 这里可以集成视频播放器 alert(播放从 formatTime(startTime) 开始的片段); // 实际应用中这里会控制视频播放器跳转到指定时间 } function copyTimecode(startTime, endTime) { const timecode [${formatTime(startTime)} - ${formatTime(endTime)}]; navigator.clipboard.writeText(timecode).then(() { alert(时间码已复制: timecode); }); } function formatTime(seconds) { const hours Math.floor(seconds / 3600); const minutes Math.floor((seconds % 3600) / 60); const secs Math.floor(seconds % 60); return ${hours.toString().padStart(2, 0)}:${minutes.toString().padStart(2, 0)}:${secs.toString().padStart(2, 0)}; } /script /body /html with open(output_file, w, encodingutf-8) as f: f.write(html_content) print(fHTML报告已生成: {output_file}) return output_file5.3 集成到SOONet Web界面如果你使用SOONet的Web界面可以添加一个导出按钮# 在Gradio界面中添加导出功能 import gradio as gr def soonet_with_export(input_text, video_file): 带导出功能的SOONet推理 # 调用SOONet result soonet_pipeline((input_text, video_file)) # 生成各种导出文件 srt_file json_to_srt(result, output.srt, input_text) edl_file json_to_edl(result, output.edl, video_file) html_file generate_html_report(result, video_file, report.html) # 返回结果和文件列表 return { result: result, files: [srt_file, edl_file, html_file] } # 创建Gradio界面 with gr.Blocks() as demo: gr.Markdown(# SOONet视频分析系统) with gr.Row(): with gr.Column(): text_input gr.Textbox(label查询文本, placeholder输入英文描述...) video_input gr.Video(label上传视频) btn gr.Button(开始分析) with gr.Column(): output_text gr.JSON(label分析结果) file_output gr.Files(label导出文件) btn.click( soonet_with_export, inputs[text_input, video_input], outputs[output_text, file_output] ) demo.launch()6. 总结通过将SOONet的JSON输出转换为可视化的SRT字幕和剪辑工程文件我们实现了从AI分析到实际应用的无缝衔接。这套方案有以下几个核心优势6.1 效率大幅提升手动查找视频片段的时间从几小时缩短到几分钟自动生成剪辑标记减少重复性工作批量处理多个查询一次性完成所有分析6.2 准确性明显改善可视化验证确保AI找得准颜色编码区分置信度重点关注高置信度片段交互式报告方便团队协作和确认6.3 工作流程更顺畅SRT文件兼容所有主流播放器EDL/XML文件直接导入剪辑软件HTML报告便于分享和演示6.4 灵活扩展性强可以根据需要定制输出格式支持批量处理和合并结果可以集成到现有工作流程中实际使用中你可以根据自己的需求调整这个方案。比如如果你主要用Final Cut Pro可以生成FCPXML格式如果你需要更详细的报告可以添加更多分析维度。最重要的是这个方案让AI的分析结果不再是冷冰冰的数据而是变成了可以直接使用的工具。下次你用SOONet分析视频时不妨试试这个方法相信会让你的工作效率提升不少。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章