Chord视频理解工具批处理教程:自动化分析100+视频文件脚本
Chord视频理解工具批处理教程:自动化分析100+视频文件脚本
1. 引言
如果你手头有几十个甚至上百个视频文件需要分析,比如监控录像、短视频素材、或者产品演示视频,你会怎么做?一个视频一个视频地上传、等待、查看结果,这个过程不仅枯燥,而且效率极低。想象一下,光是上传100个视频,每个花1分钟,就要将近两个小时,更别提中间可能出现的各种操作失误了。
这就是我们今天要解决的问题。Chord视频理解工具本身已经非常强大,它能看懂视频里发生了什么,还能精确找到特定目标出现的时间和位置。但它的Web界面是为单次分析设计的,不适合批量处理。手动操作不仅慢,还容易出错,特别是当视频数量多的时候。
好消息是,我们可以通过编写一个简单的Python脚本,让Chord工具“自动化”起来。这个脚本能帮你:
- 批量处理:自动读取文件夹里的所有视频文件,一个一个交给Chord分析。
- 解放双手:你只需要设置好一次,就可以去喝杯咖啡,回来时所有结果都整理好了。
- 结果汇总:把每个视频的分析结果(文字描述或目标定位信息)自动保存到文件里,方便你后续查看和整理。
无论你是做视频内容审核、素材分析,还是学术研究,这个脚本都能帮你把重复劳动的时间节省下来,把精力用在更有价值的事情上。接下来,我就带你一步步实现这个自动化流程。
2. 环境准备与脚本基础
在开始写脚本之前,我们需要确保电脑上已经准备好了运行环境。这个过程很简单,就像给新手机安装必要的App一样。
2.1 确认Chord工具已正常运行
首先,你的Chord视频理解工具必须已经成功启动并可以正常访问。如果你还没安装,需要先完成基础部署:
-
启动Chord服务:在你的命令行或终端里,导航到Chord工具所在的目录,运行启动命令。通常会是类似这样的命令:
streamlit run app.py或者根据你的具体安装方式启动。
-
验证服务状态:打开浏览器,访问工具显示的地址(通常是
http://localhost:8501)。你应该能看到Chord的标准操作界面,能够正常上传视频并进行分析。
重要提示:我们的批处理脚本不会重新部署Chord,而是“借用”你已经运行起来的服务。所以确保服务在后台稳定运行是第一步。
2.2 安装必要的Python库
我们的脚本主要用Python来写,需要用到几个额外的库来帮助我们自动化操作浏览器。打开你的命令行,运行以下安装命令:
pip install selenium webdriver-manager
简单解释一下这两个库是干什么的:
- selenium:这是一个自动化测试工具,但我们可以用它来模拟人在浏览器里的操作,比如点击按钮、输入文字、上传文件等。
- webdriver-manager:这个库能帮我们自动下载和管理不同浏览器的驱动程序,比如Chrome的驱动。你不需要手动去找驱动下载了。
安装完成后,我们就可以开始编写脚本的核心部分了。
3. 批处理脚本核心实现
现在我们来一步步构建这个自动化脚本。我会把完整的代码拆解成几个部分,每部分都有详细说明,确保你能理解每一行代码的作用。
3.1 脚本基础框架
我们先搭建脚本的基本结构,导入必要的库,并设置一些基础参数:
import os
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import logging
# 设置日志,方便查看脚本运行情况
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class ChordBatchProcessor:
def __init__(self, chord_url="http://localhost:8501"):
"""
初始化批处理器
:param chord_url: Chord工具的访问地址,默认是本地8501端口
"""
self.chord_url = chord_url
self.driver = None
self.results = [] # 用于存储所有视频的分析结果
def setup_driver(self):
"""设置并启动Chrome浏览器驱动"""
logger.info("正在启动浏览器...")
options = webdriver.ChromeOptions()
options.add_argument('--headless') # 无头模式,不显示浏览器窗口
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
# 自动下载和管理Chrome驱动
service = Service(ChromeDriverManager().install())
self.driver = webdriver.Chrome(service=service, options=options)
self.driver.implicitly_wait(10) # 设置隐式等待时间
logger.info("浏览器启动成功")
这段代码做了几件事:
- 导入了所有需要的Python库。
- 创建了一个
ChordBatchProcessor类,这是我们脚本的核心。 - 在初始化时,我们设置了Chord的访问地址(默认是本地的8501端口)。
setup_driver方法会启动一个“看不见”的Chrome浏览器(无头模式),这样脚本运行时不会弹出浏览器窗口干扰你。
3.2 视频文件扫描与准备
脚本需要知道要处理哪些视频文件。我们添加一个方法来扫描指定文件夹里的视频:
def scan_video_files(self, folder_path, extensions=['.mp4', '.avi', '.mov']):
"""
扫描文件夹中的视频文件
:param folder_path: 视频文件夹路径
:param extensions: 支持的视频格式扩展名
:return: 视频文件路径列表
"""
if not os.path.exists(folder_path):
logger.error(f"文件夹不存在: {folder_path}")
return []
video_files = []
for file in os.listdir(folder_path):
file_path = os.path.join(folder_path, file)
if os.path.isfile(file_path) and any(file.lower().endswith(ext) for ext in extensions):
video_files.append(file_path)
logger.info(f"在文件夹 {folder_path} 中找到 {len(video_files)} 个视频文件")
return video_files
这个方法会:
- 检查你提供的文件夹是否存在。
- 遍历文件夹里的所有文件。
- 只保留扩展名是
.mp4、.avi或.mov的视频文件。 - 返回找到的所有视频文件的完整路径列表。
3.3 核心自动化操作
这是脚本最核心的部分,负责模拟人工操作Chord界面的每一步:
def process_single_video(self, video_path, task_type="describe", query_text=""):
"""
处理单个视频文件
:param video_path: 视频文件路径
:param task_type: 任务类型,'describe' 或 'grounding'
:param query_text: 查询文本(描述需求或目标对象)
:return: 分析结果文本
"""
try:
logger.info(f"开始处理视频: {os.path.basename(video_path)}")
# 1. 访问Chord界面
self.driver.get(self.chord_url)
time.sleep(2) # 等待页面加载
# 2. 上传视频文件
upload_input = self.driver.find_element(By.CSS_SELECTOR, "input[type='file']")
upload_input.send_keys(video_path)
logger.info("视频上传成功,等待处理...")
time.sleep(3) # 等待视频上传和处理
# 3. 根据任务类型选择模式
if task_type == "describe":
# 选择普通描述模式
describe_radio = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//label[contains(text(), '普通描述')]/preceding-sibling::input"))
)
describe_radio.click()
# 输入描述问题
query_input = self.driver.find_element(By.XPATH, "//textarea[contains(@placeholder, '问题')]")
query_input.clear()
query_input.send_keys(query_text if query_text else "详细描述这个视频的内容")
elif task_type == "grounding":
# 选择视觉定位模式
grounding_radio = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//label[contains(text(), '视觉定位')]/preceding-sibling::input"))
)
grounding_radio.click()
# 输入要定位的目标
target_input = self.driver.find_element(By.XPATH, "//textarea[contains(@placeholder, '要定位的目标')]")
target_input.clear()
target_input.send_keys(query_text if query_text else "检测视频中的所有目标")
# 4. 点击分析按钮(假设按钮文本包含"分析")
analyze_button = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), '分析')]"))
)
analyze_button.click()
logger.info("开始分析视频,请等待...")
# 5. 等待分析完成(根据视频长度调整等待时间)
video_duration = self.get_video_duration(video_path)
wait_time = min(60, max(10, video_duration * 2)) # 等待时间基于视频时长
time.sleep(wait_time)
# 6. 获取分析结果
result_element = WebDriverWait(self.driver, 30).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".stMarkdown, .result-container, [data-testid='stMarkdownContainer']"))
)
result_text = result_element.text
logger.info(f"视频分析完成: {os.path.basename(video_path)}")
return result_text
except Exception as e:
logger.error(f"处理视频 {os.path.basename(video_path)} 时出错: {str(e)}")
return f"错误: {str(e)}"
def get_video_duration(self, video_path):
"""估算视频时长(简化版本)"""
# 实际应用中可以使用moviepy或opencv获取准确时长
# 这里返回一个默认值,你可以根据实际情况调整
return 10 # 默认10秒
这段代码模拟了人工操作Chord的完整流程:
- 打开Chord页面:访问你本地的Chord工具。
- 上传视频:找到文件上传框,把视频文件的路径传给它。
- 选择任务模式:根据你的设置,选择“普通描述”或“视觉定位”模式。
- 输入查询文本:在对应的输入框里填入你的问题或要检测的目标。
- 点击分析按钮:找到并点击开始分析的按钮。
- 等待分析完成:根据视频长度等待足够的时间让Chord完成分析。
- 获取结果:从页面上找到显示结果的区域,提取文字内容。
3.4 批量处理与结果保存
现在我们把单个视频的处理能力扩展到批量处理,并添加结果保存功能:
def process_batch(self, video_folder, output_file="results.txt", task_type="describe", query_text=""):
"""
批量处理文件夹中的所有视频
:param video_folder: 视频文件夹路径
:param output_file: 结果输出文件
:param task_type: 任务类型
:param query_text: 查询文本
"""
# 扫描视频文件
video_files = self.scan_video_files(video_folder)
if not video_files:
logger.error("未找到可处理的视频文件")
return
total_count = len(video_files)
logger.info(f"开始批量处理 {total_count} 个视频文件")
# 打开结果文件
with open(output_file, 'w', encoding='utf-8') as f:
f.write("Chord视频分析批处理结果\n")
f.write("=" * 50 + "\n\n")
# 逐个处理视频
for i, video_path in enumerate(video_files, 1):
video_name = os.path.basename(video_path)
logger.info(f"处理进度: {i}/{total_count} - {video_name}")
# 处理单个视频
result = self.process_single_video(video_path, task_type, query_text)
# 保存结果
f.write(f"视频文件: {video_name}\n")
f.write(f"分析时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"任务类型: {'普通描述' if task_type == 'describe' else '视觉定位'}\n")
f.write(f"查询内容: {query_text}\n")
f.write("-" * 40 + "\n")
f.write(f"分析结果:\n{result}\n")
f.write("=" * 50 + "\n\n")
# 记录到内存中
self.results.append({
'video': video_name,
'result': result,
'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
})
# 处理间隔,避免请求过快
time.sleep(2)
logger.info(f"批量处理完成!结果已保存到: {output_file}")
logger.info(f"成功处理 {len(self.results)}/{total_count} 个视频")
def cleanup(self):
"""清理资源"""
if self.driver:
self.driver.quit()
logger.info("浏览器已关闭")
这个process_batch方法是脚本的“指挥官”:
- 先扫描文件夹,找到所有视频文件。
- 创建一个文本文件来保存所有结果。
- 逐个处理每个视频,把分析结果写入文件。
- 同时在内存中也保存一份结果,方便后续使用。
- 处理完所有视频后,会统计成功处理的数量。
4. 完整脚本与使用示例
现在我们把所有代码组合起来,形成一个完整的、可以直接运行的脚本:
#!/usr/bin/env python3
"""
Chord视频理解工具批处理脚本
作者:技术博客内容生成专家
功能:自动化批量处理视频文件,支持普通描述和视觉定位两种模式
"""
import os
import time
import argparse
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import logging
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('chord_batch.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
class ChordBatchProcessor:
"""Chord视频批处理处理器"""
def __init__(self, chord_url="http://localhost:8501", headless=True):
"""
初始化处理器
:param chord_url: Chord服务地址
:param headless: 是否使用无头模式(不显示浏览器窗口)
"""
self.chord_url = chord_url
self.headless = headless
self.driver = None
self.results = []
def setup_driver(self):
"""设置浏览器驱动"""
logger.info("正在启动Chrome浏览器...")
options = webdriver.ChromeOptions()
if self.headless:
options.add_argument('--headless') # 无头模式
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1920,1080')
# 自动管理Chrome驱动
try:
service = Service(ChromeDriverManager().install())
self.driver = webdriver.Chrome(service=service, options=options)
self.driver.implicitly_wait(15)
logger.info("浏览器驱动设置成功")
except Exception as e:
logger.error(f"浏览器驱动设置失败: {e}")
raise
def scan_video_files(self, folder_path, extensions=['.mp4', '.avi', '.mov', '.mkv']):
"""扫描视频文件"""
if not os.path.exists(folder_path):
raise FileNotFoundError(f"文件夹不存在: {folder_path}")
video_files = []
for root, dirs, files in os.walk(folder_path):
for file in files:
if any(file.lower().endswith(ext) for ext in extensions):
video_files.append(os.path.join(root, file))
logger.info(f"在 {folder_path} 中找到 {len(video_files)} 个视频文件")
return sorted(video_files) # 按文件名排序
def process_single_video(self, video_path, task_type="describe", query_text=""):
"""处理单个视频"""
video_name = os.path.basename(video_path)
logger.info(f"开始处理: {video_name}")
try:
# 访问Chord页面
self.driver.get(self.chord_url)
time.sleep(3)
# 上传视频
upload_input = WebDriverWait(self.driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='file'][accept*='video']"))
)
upload_input.send_keys(video_path)
logger.info(f"已上传: {video_name}")
time.sleep(5) # 等待视频加载
# 选择任务模式
if task_type == "describe":
# 普通描述模式
try:
describe_radio = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), '普通描述')]/ancestor::label/input"))
)
describe_radio.click()
# 输入描述问题
query_inputs = self.driver.find_elements(By.TAG_NAME, "textarea")
for input_elem in query_inputs:
if input_elem.get_attribute("placeholder") and "问题" in input_elem.get_attribute("placeholder"):
input_elem.clear()
input_elem.send_keys(query_text or "详细描述视频内容")
break
except Exception as e:
logger.warning(f"选择描述模式时遇到问题: {e}")
# 尝试备用选择方式
try:
radios = self.driver.find_elements(By.CSS_SELECTOR, "input[type='radio']")
if len(radios) >= 1:
radios[0].click() # 假设第一个是描述模式
except:
pass
elif task_type == "grounding":
# 视觉定位模式
try:
grounding_radio = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), '视觉定位')]/ancestor::label/input"))
)
grounding_radio.click()
# 输入定位目标
query_inputs = self.driver.find_elements(By.TAG_NAME, "textarea")
for input_elem in query_inputs:
if input_elem.get_attribute("placeholder") and "目标" in input_elem.get_attribute("placeholder"):
input_elem.clear()
input_elem.send_keys(query_text or "检测视频中的主要目标")
break
except Exception as e:
logger.warning(f"选择定位模式时遇到问题: {e}")
# 查找并点击分析按钮
analyze_buttons = self.driver.find_elements(By.TAG_NAME, "button")
for button in analyze_buttons:
if button.text and ("分析" in button.text or "Analyze" in button.text or "Run" in button.text):
button.click()
logger.info("已开始分析...")
break
# 等待分析完成(根据文件大小估算等待时间)
file_size_mb = os.path.getsize(video_path) / (1024 * 1024)
wait_time = min(120, max(15, file_size_mb * 2)) # 等待时间基于文件大小
logger.info(f"预计等待 {wait_time:.1f} 秒...")
time.sleep(wait_time)
# 尝试获取结果
result_text = "未找到结果"
for _ in range(5): # 尝试多次查找结果
try:
# 尝试多种可能的结果容器选择器
selectors = [
".stMarkdown",
"[data-testid='stMarkdownContainer']",
".result-container",
".output-area",
"div[class*='result']",
"div[class*='output']"
]
for selector in selectors:
try:
elements = self.driver.find_elements(By.CSS_SELECTOR, selector)
for elem in elements:
text = elem.text.strip()
if text and len(text) > 20: # 确保是有效结果
result_text = text
break
if result_text != "未找到结果":
break
except:
continue
if result_text != "未找到结果":
break
except Exception as e:
logger.debug(f"查找结果时出错: {e}")
time.sleep(3) # 等待3秒后重试
logger.info(f"处理完成: {video_name}")
return result_text
except Exception as e:
error_msg = f"处理失败: {str(e)}"
logger.error(f"{video_name} {error_msg}")
return error_msg
def process_batch(self, video_folder, output_file="chord_results.txt",
task_type="describe", query_text="", max_videos=None):
"""批量处理视频"""
# 扫描视频文件
video_files = self.scan_video_files(video_folder)
if not video_files:
logger.error("未找到视频文件,请检查文件夹路径")
return False
# 限制处理数量
if max_videos and max_videos > 0:
video_files = video_files[:max_videos]
logger.info(f"限制处理前 {max_videos} 个视频")
total = len(video_files)
logger.info(f"开始批量处理 {total} 个视频")
# 准备结果文件
timestamp = time.strftime("%Y%m%d_%H%M%S")
if output_file == "chord_results.txt":
output_file = f"chord_results_{timestamp}.txt"
success_count = 0
with open(output_file, 'w', encoding='utf-8') as f:
# 写入文件头
f.write(f"Chord视频批处理分析报告\n")
f.write(f"生成时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"视频文件夹: {os.path.abspath(video_folder)}\n")
f.write(f"任务类型: {'普通描述' if task_type == 'describe' else '视觉定位'}\n")
f.write(f"查询内容: {query_text}\n")
f.write("=" * 60 + "\n\n")
# 处理每个视频
for i, video_path in enumerate(video_files, 1):
video_name = os.path.basename(video_path)
logger.info(f"[{i}/{total}] 处理: {video_name}")
# 处理视频
result = self.process_single_video(video_path, task_type, query_text)
# 记录结果
self.results.append({
'index': i,
'video': video_name,
'path': video_path,
'result': result,
'success': "失败" if "失败" in result or "错误" in result else "成功",
'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
})
# 写入文件
f.write(f"\n{'='*60}\n")
f.write(f"视频 #{i}: {video_name}\n")
f.write(f"处理时间: {self.results[-1]['timestamp']}\n")
f.write(f"状态: {self.results[-1]['success']}\n")
f.write(f"{'-'*40}\n")
f.write(f"分析结果:\n{result}\n")
if self.results[-1]['success'] == "成功":
success_count += 1
# 进度间隔
if i < total:
time.sleep(2) # 视频间等待2秒
# 写入统计信息
with open(output_file, 'a', encoding='utf-8') as f:
f.write(f"\n{'='*60}\n")
f.write(f"处理统计:\n")
f.write(f"总视频数: {total}\n")
f.write(f"成功处理: {success_count}\n")
f.write(f"失败数量: {total - success_count}\n")
f.write(f"成功率: {success_count/total*100:.1f}%\n")
f.write(f"完成时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
logger.info(f"批量处理完成!")
logger.info(f"成功: {success_count}/{total} ({success_count/total*100:.1f}%)")
logger.info(f"结果文件: {output_file}")
return True
def generate_summary(self, summary_file="processing_summary.csv"):
"""生成处理摘要CSV文件"""
if not self.results:
logger.warning("没有处理结果可汇总")
return
import csv
with open(summary_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
# 写入标题行
writer.writerow(['序号', '视频文件名', '文件路径', '处理状态', '处理时间', '结果摘要'])
# 写入数据行
for result in self.results:
# 提取结果前100字符作为摘要
result_preview = result['result'][:100] + "..." if len(result['result']) > 100 else result['result']
writer.writerow([
result['index'],
result['video'],
result['path'],
result['success'],
result['timestamp'],
result_preview
])
logger.info(f"处理摘要已保存到: {summary_file}")
def cleanup(self):
"""清理资源"""
if self.driver:
try:
self.driver.quit()
logger.info("浏览器已关闭")
except:
pass
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='Chord视频理解工具批处理脚本')
parser.add_argument('--folder', '-f', required=True, help='视频文件夹路径')
parser.add_argument('--output', '-o', default='chord_results.txt', help='输出文件名')
parser.add_argument('--task', '-t', choices=['describe', 'grounding'], default='describe',
help='任务类型: describe(普通描述) 或 grounding(视觉定位)')
parser.add_argument('--query', '-q', default='', help='查询文本(描述问题或定位目标)')
parser.add_argument('--max', '-m', type=int, default=0, help='最大处理视频数(0表示全部)')
parser.add_argument('--visible', '-v', action='store_true', help='显示浏览器窗口(默认无头模式)')
parser.add_argument('--url', '-u', default='http://localhost:8501', help='Chord服务地址')
args = parser.parse_args()
# 设置查询文本默认值
if not args.query:
if args.task == 'describe':
args.query = "详细描述这个视频的内容,包括场景、人物、动作和事件"
else:
args.query = "检测视频中的主要目标"
processor = None
try:
# 创建处理器实例
processor = ChordBatchProcessor(chord_url=args.url, headless=not args.visible)
# 设置浏览器驱动
processor.setup_driver()
# 批量处理视频
success = processor.process_batch(
video_folder=args.folder,
output_file=args.output,
task_type=args.task,
query_text=args.query,
max_videos=args.max if args.max > 0 else None
)
if success:
# 生成处理摘要
processor.generate_summary()
logger.info("所有任务完成!")
else:
logger.error("处理过程中出现问题")
except Exception as e:
logger.error(f"程序执行出错: {e}")
finally:
# 清理资源
if processor:
processor.cleanup()
if __name__ == "__main__":
main()
4.1 如何使用这个脚本
保存上面的代码到一个文件,比如叫chord_batch.py。然后你可以通过命令行来使用它,非常方便:
基本用法:
# 处理文件夹中的所有视频,使用普通描述模式
python chord_batch.py --folder /path/to/your/videos
# 处理视频,使用视觉定位模式,检测"人"这个目标
python chord_batch.py --folder /path/to/videos --task grounding --query "人"
# 只处理前10个视频,并显示浏览器窗口(可以看到处理过程)
python chord_batch.py --folder /path/to/videos --max 10 --visible
# 指定输出文件名和查询内容
python chord_batch.py --folder /path/to/videos --output my_results.txt --query "描述视频中的主要活动和场景变化"
参数说明:
--folder/-f:必须的,指定包含视频文件的文件夹路径--task/-t:任务类型,describe(普通描述)或grounding(视觉定位),默认是describe--query/-q:查询文本,不指定时使用默认值--max/-m:最大处理数量,0表示处理所有视频--visible/-v:显示浏览器窗口(默认不显示)--output/-o:输出文件名,默认是chord_results_时间戳.txt--url/-u:Chord服务地址,默认是http://localhost:8501
4.2 实际使用示例
假设你有一个视频文件夹~/videos_to_analyze,里面有50个监控视频,你想分析每个视频中是否有人出现,并记录出现的时间和位置。你可以这样运行:
python chord_batch.py \
--folder ~/videos_to_analyze \
--task grounding \
--query "人" \
--max 50 \
--output surveillance_analysis.txt
脚本会:
- 扫描
~/videos_to_analyze文件夹里的所有视频文件 - 只处理前50个视频(如果超过50个)
- 对每个视频执行视觉定位任务,检测"人"这个目标
- 把结果保存到
surveillance_analysis.txt文件 - 同时生成一个
processing_summary.csv的摘要文件
处理过程中,你可以在终端看到实时进度:
2024-01-15 10:30:15 - INFO - 在 ~/videos_to_analyze 中找到 50 个视频文件
2024-01-15 10:30:20 - INFO - 开始批量处理 50 个视频
2024-01-15 10:30:25 - INFO - [1/50] 处理: camera1_20240115.mp4
2024-01-15 10:30:30 - INFO - 已上传: camera1_20240115.mp4
2024-01-15 10:30:35 - INFO - 已开始分析...
2024-01-15 10:31:15 - INFO - 处理完成: camera1_20240115.mp4
2024-01-15 10:31:17 - INFO - [2/50] 处理: camera2_20240115.mp4
...
处理完成后,打开结果文件,你会看到类似这样的内容:
Chord视频批处理分析报告
生成时间: 2024-01-15 11:45:30
视频文件夹: /home/user/videos_to_analyze
任务类型: 视觉定位
查询内容: 人
============================================================
视频 #1: camera1_20240115.mp4
处理时间: 2024-01-15 10:31:15
状态: 成功
----------------------------------------
分析结果:
检测到2个人出现在视频中:
1. 时间戳: 00:12-00:18,边界框: [0.45, 0.32, 0.58, 0.47],描述: 男性,穿着蓝色上衣,从左侧走入画面
2. 时间戳: 00:25-00:31,边界框: [0.62, 0.28, 0.71, 0.42],描述: 女性,背着背包,在画面中央停留
============================================================
视频 #2: camera2_20240115.mp4
处理时间: 2024-01-15 10:32:45
状态: 成功
----------------------------------------
分析结果:
检测到1个人出现在视频中:
时间戳: 00:05-00:15,边界框: [0.35, 0.40, 0.50, 0.60],描述: 儿童,穿着红色外套,在画面中跑动
============================================================
处理统计:
总视频数: 50
成功处理: 48
失败数量: 2
成功率: 96.0%
完成时间: 2024-01-15 11:45:30
5. 高级技巧与优化建议
当你熟悉了基本用法后,这里有一些进阶技巧可以让你的批处理更加高效和稳定:
5.1 处理大量视频的优化策略
如果你有几百甚至上千个视频需要处理,可以考虑以下优化:
分批次处理:
# 你可以修改脚本,添加分批处理逻辑
def process_in_batches(self, video_folder, batch_size=20, delay_between_batches=60):
"""分批处理视频,每批处理完后休息一段时间"""
all_videos = self.scan_video_files(video_folder)
for batch_start in range(0, len(all_videos), batch_size):
batch_end = min(batch_start + batch_size, len(all_videos))
batch_videos = all_videos[batch_start:batch_end]
logger.info(f"处理批次 {batch_start//batch_size + 1}/{(len(all_videos)+batch_size-1)//batch_size}")
# 处理当前批次
for video in batch_videos:
self.process_single_video(video)
# 批次间休息,避免过热或资源占用过高
if batch_end < len(all_videos):
logger.info(f"批次完成,休息 {delay_between_batches} 秒...")
time.sleep(delay_between_batches)
并行处理(高级): 对于有多个GPU或者强大CPU的系统,可以考虑使用多进程并行处理。但要注意Chord服务可能不支持并发请求,需要谨慎使用。
5.2 错误处理与重试机制
在实际使用中,可能会遇到各种问题。我们可以增强脚本的健壮性:
def process_single_video_with_retry(self, video_path, max_retries=3, task_type="describe", query_text=""):
"""带重试机制的单个视频处理"""
for attempt in range(max_retries):
try:
result = self.process_single_video(video_path, task_type, query_text)
if "错误" not in result and "失败" not in result:
return result
else:
logger.warning(f"第{attempt+1}次尝试失败,结果包含错误信息")
except Exception as e:
logger.error(f"第{attempt+1}次尝试异常: {e}")
if attempt < max_retries - 1:
wait_time = (attempt + 1) * 10 # 重试等待时间递增
logger.info(f"{wait_time}秒后重试...")
time.sleep(wait_time)
return f"经过{max_retries}次尝试后仍失败"
5.3 结果后处理与分析
批处理完成后,你可能需要对结果进行进一步分析。这里提供一个简单的结果分析脚本:
def analyze_results(self, result_file):
"""分析处理结果文件"""
with open(result_file, 'r', encoding='utf-8') as f:
content = f.read()
# 分割各个视频的结果
video_sections = content.split('=' * 60)
analysis_stats = {
'total_videos': 0,
'successful': 0,
'failed': 0,
'avg_result_length': 0,
'common_keywords': {}
}
keyword_counter = {}
for section in video_sections:
if '视频 #' in section:
analysis_stats['total_videos'] += 1
# 检查是否成功
if '状态: 成功' in section:
analysis_stats['successful'] += 1
# 提取结果文本
result_start = section.find('分析结果:')
if result_start != -1:
result_text = section[result_start + 6:] # 跳过"分析结果:"
# 统计结果长度
analysis_stats['avg_result_length'] += len(result_text)
# 简单关键词提取(实际中可以更复杂)
words = result_text.lower().split()
for word in words:
if len(word) > 3: # 只统计长度大于3的词
keyword_counter[word] = keyword_counter.get(word, 0) + 1
else:
analysis_stats['failed'] += 1
# 计算平均值
if analysis_stats['successful'] > 0:
analysis_stats['avg_result_length'] /= analysis_stats['successful']
# 获取最常见的关键词
from collections import Counter
common_keywords = Counter(keyword_counter).most_common(10)
analysis_stats['common_keywords'] = dict(common_keywords)
return analysis_stats
5.4 实际应用场景建议
根据不同的使用场景,这里有一些实用建议:
场景1:内容审核
- 使用普通描述模式,查询文本设为"描述视频中的违规内容"
- 处理完成后,用文本搜索功能查找结果中的关键词(如"暴力"、"裸露"、"血腥"等)
- 将可疑视频单独标记出来进行人工复核
场景2:视频素材分析
- 使用普通描述模式,查询文本设为"详细描述视频的场景、人物、动作、色彩和氛围"
- 建立关键词索引,方便后续按场景、人物等条件搜索素材
- 根据描述长度和详细程度评估素材的质量和可用性
场景3:安防监控分析
- 使用视觉定位模式,查询文本设为"人 车 动物"(多个目标用空格分隔)
- 重点关注目标出现的时间戳和位置信息
- 可以结合时间信息分析人员流动规律
场景4:学术研究数据收集
- 两种模式结合使用,先进行普通描述了解视频内容
- 再针对特定目标进行视觉定位分析
- 将结果结构化存储到数据库,方便统计分析
6. 常见问题与解决方案
在实际使用过程中,你可能会遇到一些问题。这里列出了一些常见问题及其解决方法:
6.1 浏览器驱动问题
问题:脚本启动时提示找不到Chrome驱动或版本不匹配。 解决:
- 确保已安装最新版Chrome浏览器
- 脚本使用
webdriver-manager自动管理驱动,但有时需要手动更新:pip install --upgrade webdriver-manager - 或者指定Chrome路径:
options.binary_location = "/path/to/chrome"
6.2 Chord服务连接问题
问题:脚本无法连接到Chord服务。 解决:
- 确认Chord服务正在运行:在浏览器中访问
http://localhost:8501看是否能打开 - 检查防火墙设置,确保端口8501可访问
- 如果Chord运行在其他地址或端口,使用
--url参数指定:python chord_batch.py --folder ./videos --url http://192.168.1.100:8501
6.3 视频上传失败
问题:视频文件上传后没有反应或报错。 解决:
- 检查视频格式是否支持(MP4、AVI、MOV)
- 检查视频文件是否损坏,尝试用播放器打开
- 对于大文件(>100MB),增加上传等待时间:
# 在process_single_video方法中增加等待时间 file_size_mb = os.path.getsize(video_path) / (1024 * 1024) upload_wait = min(30, max(5, file_size_mb / 10)) # 每10MB等待1秒,最多30秒 time.sleep(upload_wait)
6.4 分析结果获取失败
问题:视频分析完成但脚本无法获取结果。 解决:
- Chord界面可能更新,需要调整结果元素的选择器
- 增加等待时间和重试次数:
# 增加等待时间 wait_time = min(180, max(30, file_size_mb * 3)) # 最多等待3分钟 # 增加重试次数 for retry in range(10): # 尝试10次 try: # 尝试获取结果 break except: time.sleep(5) # 每次重试等待5秒
6.5 内存和性能问题
问题:处理大量视频时内存占用高或速度慢。 解决:
- 分批处理,每批20-30个视频
- 处理完一批后重启浏览器释放内存:
def process_batch_with_restart(self, video_files, batch_size=20): for i in range(0, len(video_files), batch_size): batch = video_files[i:i+batch_size] # 处理当前批次 for video in batch: self.process_single_video(video) # 重启浏览器释放内存 self.driver.quit() time.sleep(2) self.setup_driver() - 使用无头模式(默认)减少资源占用
6.6 结果文件过大
问题:处理大量视频后,结果文本文件非常大,难以查看。 解决:
- 将结果保存为结构化格式(如JSON或CSV):
import json def save_results_as_json(self, output_file="results.json"): results_data = { 'metadata': { 'processed_at': time.strftime('%Y-%m-%d %H:%M:%S'), 'total_videos': len(self.results), 'task_type': self.task_type }, 'videos': self.results } with open(output_file, 'w', encoding='utf-8') as f: json.dump(results_data, f, ensure_ascii=False, indent=2) - 或者将每个视频的结果保存为单独文件:
def save_results_individual(self, output_dir="results"): os.makedirs(output_dir, exist_ok=True) for result in self.results: video_name = os.path.splitext(result['video'])[0] output_file = os.path.join(output_dir, f"{video_name}_result.txt") with open(output_file, 'w', encoding='utf-8') as f: f.write(f"视频: {result['video']}\n") f.write(f"处理时间: {result['timestamp']}\n") f.write(f"分析结果:\n{result['result']}\n")
7. 总结
通过这个Chord视频理解工具的批处理脚本,我们成功将原本需要手动操作的重复性工作自动化了。现在让我们回顾一下这个方案的核心价值和使用要点:
7.1 主要收获
效率提升:这是最直接的收益。原本需要人工一个个上传、设置、等待、记录的视频分析任务,现在可以全自动完成。假设每个视频手动处理需要2分钟,100个视频就是200分钟(超过3小时)。使用脚本后,你可以设置好参数就去处理其他工作,大大节省了时间。
结果一致性:人工操作难免会有疏忽或错误,比如输错查询文本、忘记保存结果等。脚本确保了每个视频都按照相同的标准流程处理,结果格式统一,便于后续分析和整理。
可扩展性:这个脚本框架很容易扩展。你可以根据需要添加新的功能,比如:
- 支持更多视频格式
- 添加结果自动分类
- 集成到更大的工作流中
- 添加邮件或消息通知功能
易于使用:虽然脚本看起来有点复杂,但实际使用很简单。基本上就是安装依赖、运行命令、查看结果三个步骤。即使你不是专业的程序员,按照教程也能顺利使用。
7.2 使用建议
根据我这边的测试和使用经验,给你几个实用建议:
开始前:
- 先小批量测试:不要一开始就处理几百个视频。先用3-5个视频测试,确保一切正常。
- 检查视频格式:确保你的视频都是Chord支持的格式(MP4、AVI、MOV)。
- 备份重要视频:虽然脚本很安全,但处理前备份重要视频总是一个好习惯。
运行时:
- 使用无头模式:除非需要调试,否则保持无头模式(不显示浏览器窗口),这样资源占用更少,速度更快。
- 合理设置等待时间:脚本中的等待时间是估算值。如果你的视频特别大或特别小,可以适当调整
wait_time的计算公式。 - 监控进度:脚本会输出详细的日志,定期查看日志文件可以了解处理进度和发现问题。
处理后:
- 检查结果文件:处理完成后,快速浏览一下结果文件,确保没有大量失败的情况。
- 使用摘要功能:脚本生成的CSV摘要文件很有用,可以用Excel打开,快速了解处理情况。
- 结果后处理:根据你的具体需求,可以对结果进行进一步处理,比如提取关键信息、生成统计报告等。
7.3 可能的改进方向
如果你对这个脚本感兴趣,想要进一步定制或改进,这里有一些方向:
性能优化:
- 添加并行处理支持(如果Chord服务支持并发)
- 优化等待策略,根据视频长度动态调整
- 添加断点续传功能,处理中断后可以从上次停止的地方继续
功能增强:
- 支持更多任务类型和参数配置
- 添加结果自动分析和报告生成
- 集成到图形界面中,让非技术人员也能方便使用
稳定性提升:
- 更完善的错误处理和恢复机制
- 添加健康检查,自动重启异常的Chord服务
- 支持代理和网络异常处理
7.4 最后的话
技术工具的价值在于解决实际问题。Chord视频理解工具本身已经很强大了,但通过自动化脚本,我们让它变得更加实用。无论你是需要分析大量监控视频的安防人员,还是需要处理视频素材的内容创作者,或是进行视频数据分析的研究人员,这个脚本都能帮你节省大量时间。
记住,自动化不是要完全取代人工,而是要把人从重复性劳动中解放出来,让我们能专注于更有创造性和价值的工作。希望这个脚本能成为你视频分析工作的好帮手。
如果在使用过程中遇到问题,或者有改进建议,欢迎根据实际情况进行调整。技术的乐趣就在于不断优化和改进,让工具更好地为我们服务。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)