【原创】基于视觉大模型gemma-3-4b实现短视频自动识别内容并生成解说文案
📦 一、整体功能定位
这是一个用于从原始视频自动生成短视频解说内容的自动化工具,包含:
-
视频抽帧(可基于画面变化提取关键帧)
-
多模态图像识别(每帧图片理解)
-
文案生成(大模型生成口语化解说)
-
TTS语音合成(将文案变为解说音频)
-
视频合成(图片 + 音频 or 原视频音频)
-
日志记录 + 文案保存
适合用于内容创作、短视频自动剪辑辅助、图文转视频等场景。
🧱 二、代码架构总览
project/
├── main.py # 主流程逻辑入口
├── config.ini # 配置文件(模型URL、key等)
├── doubaoTTS.py # 语音合成模块(豆包接口)
├── output/ # 输出目录(帧图、文案、音频、视频)
│ ├── frames/
│ ├── narration.mp3
│ ├── frame_descriptions.txt
│ ├── narration_script.txt
│ └── final_output_with_original_audio.mp4
⚙️ 三、主要模块说明
1. extract_keyframes_by_diff()
– 拆帧模块(图像变化判断)
-
从视频中按设定帧率提取帧
-
对比相邻帧内容,过滤重复或近似帧
-
降低识图调用频率,提高信息效率
2. describe_image()
– 图像识别模块
-
使用
OLLAMA
的多模态模型(如gemma3:4b
) -
将每帧图像转 base64,提交模型识别
-
输出自然语言描述(中文)
3. generate_script()
– 文案生成模块
-
使用
OpenRouter
接口,调用如deepseek
模型 -
以所有帧描述为输入,生成整体解说词
-
提示词优化为:短视频风格 + 口语化 + 幽默
4. synthesize_audio()
– 音频合成模块
-
调用
doubaoTTS
实现 TTS 中文语音合成 -
输出
.mp3
格式音频文件
5. compose_video_with_audio()
– 视频合成模块
-
将图像序列按顺序合成为视频
-
配上:
-
合成解说音频,或
-
原视频音频(当前采用)
-
6. main()
– 主流程协调函数
执行顺序如下:
原始视频↓
图像抽帧(基于变化)↓
每帧图像识别 → 收集所有描述↓
文案生成(整体解说)↓
保存描述 + 文案↓
语音合成↓
合成视频( 原视频+音频)
🛠 四、配置文件说明(config.ini
)
[ollama]
url = http://localhost:11434/api/generate
model = gemma3:4b[openrouter]
url = https://openrouter.ai/api/v1/chat/completions
api_key = sk-xxx
model = deepseek-chat
🔁 五、可扩展方向建议
功能 | 实现思路 |
---|---|
加字幕 | 用 MoviePy 在视频帧上叠加文案 |
多语言支持 | 替换 TTS 模型或调用多语种 GPT 模型 |
语音识别 | 加入 whisper 抽取原视频语音作为额外提示输入 |
图像字幕识别 | 加 OCR 模块识别画面内字幕,辅助理解 |
批量处理视频 | 使用 CLI 参数或脚本批量遍历目录 |
Web 界面化 | 用 Gradio / Streamlit 构建可视化上传和预览界面 |
直接上代码:
import os
import requests
import json
import base64
import logging
from PIL import Image
from moviepy.editor import VideoFileClip, ImageSequenceClip, AudioFileClip
import doubaoTTS
import configparser
from PIL import Image, ImageChops
import numpy as np# 设置日志
logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(message)s')# 从 config.ini 加载配置
config = configparser.ConfigParser()
config.read("config.ini", encoding="utf-8")OLLAMA_URL = config.get("ollama", "url")
OLLAMA_MODEL = config.get("ollama", "model")
OPENROUTER_URL = config.get("openrouter", "url")
OPENROUTER_API_KEY = config.get("openrouter", "api_key")
OPENROUTER_MODEL = config.get("openrouter", "model")# 拆帧函数
def extract_keyframes_by_diff(video_path, output_dir, diff_threshold=30, fps=2, max_frames=None):"""从视频中提取关键帧(基于图像变化)参数:video_path: 视频文件路径output_dir: 输出帧目录diff_threshold: 像素差阈值,越小越敏感fps: 初始扫描帧率(例如2即每秒取2帧,再做变化判断)max_frames: 最大输出帧数,默认不限制"""os.makedirs(output_dir, exist_ok=True)clip = VideoFileClip(video_path)last_frame = Nonecount = 0logging.info(f"开始按帧提取,初始采样帧率:{fps},变化阈值:{diff_threshold}")for i, frame in enumerate(clip.iter_frames(fps=fps)):if max_frames and count >= max_frames:breakimg = Image.fromarray(frame)if last_frame:# 灰度差异diff = ImageChops.difference(img.convert('L'), last_frame.convert('L'))diff_score = np.mean(np.array(diff))if diff_score < diff_threshold:continue # 差异太小,跳过当前帧# 保存关键帧frame_path = os.path.join(output_dir, f"keyframe_{count:05d}.jpg")img.save(frame_path)last_frame = imgcount += 1logging.info(f"关键帧提取完成,共保留 {count} 帧")return sorted([os.path.join(output_dir, f) for f in os.listdir(output_dir) if f.endswith(".jpg")])# 用 gemma3:4b 多模态识图
def describe_image(img_path):logging.info(f"正在识别图片内容:{img_path}")with open(img_path, "rb") as f:image_bytes = f.read()image_b64 = base64.b64encode(image_bytes).decode("utf-8")#logging.info(image_b64)payload = {"model": OLLAMA_MODEL,"messages": [{"role": "user","content": "请用中文描述这张图片的内容,不要输出markdown格式","images": [image_b64]}],"stream": False}response = requests.post(OLLAMA_URL, json=payload)#logging.info(response.json())result = response.json().get('message', {}).get('content', '').strip()logging.info(f"识别结果:{result}")return result# 用 deepseek 生成中文文案(带完整验证与日志)
def generate_script(prompt):logging.info("正在生成视频文案")try:response = requests.post(OPENROUTER_URL,headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}","Content-Type": "application/json"},json={"model": OPENROUTER_MODEL,"messages": [{"role": "user", "content": prompt}],"stream": False})response.raise_for_status()data = response.json()if "choices" in data and data["choices"]:result = data["choices"][0]["message"]["content"]logging.info(f"记录生成的文案内容:{result}")return resultlogging.error("API 响应中没有 choices 字段: %s", data)return "生成失败,API 响应格式异常。"except requests.RequestException as e:logging.error("API 请求失败: %s", e)return "生成失败,请检查网络或服务状态。"# 合成音频
def synthesize_audio(text, output_path):logging.info("正在合成解说音频")doubaoTTS.tts_doubao(text, output_path)logging.info(f"音频保存到:{output_path}")# 合成视频
def compose_video_with_audio(frame_dir, audio_path, output_path, fps=1):logging.info("正在合成最终视频")frame_paths = sorted([os.path.join(frame_dir, f) for f in os.listdir(frame_dir) if f.endswith(".jpg")])clip = ImageSequenceClip(frame_paths, fps=fps)audio = AudioFileClip(audio_path)video = clip.set_audio(audio)video.write_videofile(output_path, codec='libx264', audio_codec='aac')logging.info(f"视频生成成功:{output_path}")def main(video_path, work_dir, fps=1):frame_dir = os.path.join(work_dir, "frames")os.makedirs(frame_dir, exist_ok=True)# 1. 拆帧frame_dir = os.path.join(work_dir, "frames")extract_keyframes_by_diff(video_path, frame_dir, diff_threshold=25, fps=2, max_frames=30)# 2. 多帧识图 + 容错处理descriptions = []frame_files = sorted([f for f in os.listdir(frame_dir) if f.lower().endswith(".jpg")])for i, frame_file in enumerate(frame_files):frame_path = os.path.join(frame_dir, frame_file)try:description = describe_image(frame_path)if description:descriptions.append(f"第{i+1}帧:{description}")else:logging.warning(f"帧 {frame_file} 描述为空,已跳过")except Exception as e:logging.error(f"处理帧 {frame_file} 时发生错误:{e}")continueif not descriptions:logging.error("没有任何帧成功识别,流程中止")return# 3. 保存描述到文案文件full_description = "\n".join(descriptions)description_txt_path = os.path.join(work_dir, "frame_descriptions.txt")with open(description_txt_path, "w", encoding="utf-8") as f:f.write(full_description)logging.info(f"帧内容描述已保存到:{description_txt_path}")# 4. 文案生成prompt = ("你是一个擅长写短视频解说词的创作者,现在请根据以下每一帧画面内容,""用轻松幽默的语气,生成一段通俗易懂、符合短视频风格的中文解说,""要求内容简洁、节奏明快、具有代入感,可以适当加入网络流行语或比喻,""但不要生硬搞笑,也不要重复描述画面,请让内容听起来像是一个博主在视频里自然说话:\n"f"{full_description}"
)script = generate_script(prompt)# 5. 保存文案内容script_txt_path = os.path.join(work_dir, "narration_script.txt")with open(script_txt_path, "w", encoding="utf-8") as f:f.write(script)logging.info(f"生成的文案已保存到:{script_txt_path}")# 6. 合成解说音频audio_path = os.path.join(work_dir, "narration.mp3")synthesize_audio(script, audio_path)# 7. 合成视频(使用原视频音频)logging.info("正在加载原视频音频")original_clip = VideoFileClip(video_path)original_audio = original_clip.audioframe_paths = sorted([os.path.join(frame_dir, f) for f in os.listdir(frame_dir) if f.lower().endswith(".jpg")])image_clip = ImageSequenceClip(frame_paths, fps=fps).set_audio(original_audio)output_video_path = os.path.join(work_dir, "final_output_with_original_audio.mp4")image_clip.write_videofile(output_video_path, codec='libx264', audio_codec='aac')logging.info(f"视频已成功生成:{output_video_path}")# 示例调用
main("2025-05-11_15-12-01_UTC.mp4", "output", fps=1) # 请取消注释后运行
相关文章:

【原创】基于视觉大模型gemma-3-4b实现短视频自动识别内容并生成解说文案
📦 一、整体功能定位 这是一个用于从原始视频自动生成短视频解说内容的自动化工具,包含: 视频抽帧(可基于画面变化提取关键帧) 多模态图像识别(每帧图片理解) 文案生成(大模型生成…...

Spark(32)SparkSQL操作Mysql
(一)准备mysql环境 我们计划在hadoop001这台设备上安装mysql服务器,(当然也可以重新使用一台全新的虚拟机)。 以下是具体步骤: 使用finalshell连接hadoop001.查看是否已安装MySQL。命令是: rpm -qa|grep ma…...

基于 Python 的界面程序复现:标准干涉槽型设计计算及仿真
基于 Python 的界面程序复现:标准干涉槽型设计计算及仿真 在工业设计与制造领域,刀具的设计与优化是提高生产效率和产品质量的关键环节之一。本文将介绍如何使用 Python 复现一个用于标准干涉槽型设计计算及仿真的界面程序,旨在帮助工程师和…...

c++成员函数返回类对象引用和直接返回类对象的区别
c成员函数返回类对象引用和直接返回类对象的区别 成员函数直接返回类对象(返回临时对象,对象拷贝) #include <iostream> class MyInt { public:int value;//构造函数explicit MyInt(int v0) : value(v){}//加法操作,返回对象副本&…...
AGI大模型(20):混合检索之rank_bm25库来实现词法搜索
1 混合检索简介 混合搜索结合了两种检索信息的方法 词法搜索 (BM25) :这种传统方法根据精确的关键字匹配来检索文档。例如,如果您搜索“cat on the mat”,它将找到包含这些确切单词的文档。 基于嵌入的搜索(密集检索) :这种较新的方法通过比较文档的语义来检索文档。查…...

数字化转型- 数字化转型路线和推进
数字化转型三个阶段 百度百科给出的企业的数字化转型包括信息化、数字化、数智化三个阶段 信息化是将企业在生产经营过程中产生的业务信息进行记录、储存和管理,通过电子终端呈现,便于信息的传播与沟通。数字化通过打通各个系统的互联互通,…...
字体样式集合
根据您提供的字体样式列表,以下是分类整理后的完整字体样式名称(不含数量统计): 基础样式 • Regular • Normal • Plain • Medium • Bold • Black • Light • Thin • Heavy • Ultra • Extra • Semi • Hai…...

IP68防水Type-C连接器实测:水下1米浸泡72小时的生存挑战
IP68防水Type-C连接器正成为户外设备、水下仪器和高端消费电子的核心组件。其宣称的“1米水深防护”是否真能抵御长时间浸泡?我们通过极限实测,将三款主流品牌IP68防水Type-C连接器沉入1米盐水(模拟海水浓度)中持续72小时…...

【技术追踪】InverseSR:使用潜在扩散模型进行三维脑部 MRI 超分辨率重建(MICCAI-2023)
LDM 实现三维超分辨率~ 论文:InverseSR: 3D Brain MRI Super-Resolution Using a Latent Diffusion Model 代码:https://github.com/BioMedAI-UCSC/InverseSR 0、摘要 从研究级医疗机构获得的高分辨率(HR)MRI 扫描能够提供关于成像…...
React学习(二)-变量
也是很无聊,竟然写这玩意,毕竟不是学术研究,普通工作没那么多概念性东西,会用就行╮(╯▽╰)╭ 在React中,变量是用于存储和管理数据的基本单位。根据其用途和生命周期,React中的变量可以分为以下几类: 1. 状态变量(State) 用途:用于存储组件的内部状态,状态变化会触…...

list重点接口及模拟实现
list功能介绍 c中list是使用双向链表实现的一个容器,这个容器可以实现。插入,删除等的操作。与vector相比,vector适合尾插和尾删(vector的实现是使用了动态数组的方式。在进行头删和头插的时候后面的数据会进行挪动,时…...
【自然语言处理与大模型】大模型(LLM)基础知识④
(1)微调主要用来干什么? 微调目前最主要用在定制模型的自我认知和改变模型对话风格。模型能力的适配与强化只是辅助。 定制模型的自我认知:通过微调可以调整模型对自我身份、角色功能的重新认知,使其回答更加符合自定义…...
系统架构设计(九):分布式架构与微服务
基础定义 架构类型定义分布式架构指将系统部署在多个服务器节点上,通过网络协作完成整体功能。强调物理上的分布与任务协作。微服务架构一种分布式架构模式,将系统按照业务维度拆分为多个小型自治服务,每个服务可独立开发、部署、伸缩。 核…...

Java 框架配置自动化:告别冗长的 XML 与 YAML 文件
在 Java 开发领域,框架的使用极大地提升了开发效率和系统的稳定性。然而,传统框架配置中冗长的 XML 与 YAML 文件,却成为开发者的一大困扰。这些配置文件不仅书写繁琐,容易出现语法错误,而且在项目规模扩大时ÿ…...

vue使用Pinia实现不同页面共享token
文章目录 一、概述二、使用步骤安装pinia在vue应用实例中使用pinia在src/stores/token.js中定义store在组件中使用store登录成功后,将token保存pinia中向后端API发起请求时,携带从pinia中获取的token 三、参考资料 一、概述 Pinia是Vue的专属状态管理库…...

遨游科普:三防平板是什么?有什么功能?
清晨的露珠还挂在帐篷边缘,背包里的三防平板却已开机导航;工地的尘土飞扬中,工程师正通过它查看施工图纸;暴雨倾盆的救援现场,应急队员用它实时回传灾情数据……这些看似科幻的场景,正因三防平板的普及成为…...

spring MVC 至 springboot的发展流程,配置文件变化
spring mvc Spring MVC 是 Spring 框架中的一个重要模块,用于构建基于 Java 的 Web 应用程序。它基于 MVC(Model-View-Controller)设计模式,提供了灵活、可配置的方式来开发动态网页或 RESTful 服务 ssm SSM 框架…...
深入解析Spring Boot与JUnit 5的集成测试实践
深入解析Spring Boot与JUnit 5的集成测试实践 引言 在现代软件开发中,测试是确保代码质量和功能正确性的关键环节。Spring Boot作为目前最流行的Java Web框架之一,提供了强大的支持来简化测试流程。而JUnit 5作为最新的JUnit版本,引入了许多…...

AI全域智能监控系统重构商业清洁管理范式——从被动响应到主动预防的监控效能革命
一、四维立体监控网络技术架构 1. 人员行为监控 - 融合人脸识别、骨骼追踪与RFID工牌技术,身份识别准确率99.97% - 支持15米超距夜间红外监控(精度0.01lux) 2. 作业过程监控 - UWB厘米级定位技术(误差<0.3米&…...

网络编程中的直接内存与零拷贝
本篇文章会介绍 JDK 与 Linux 网络编程中的直接内存与零拷贝的相关知识,最后还会介绍一下 Linux 系统与 JDK 对网络通信的实现。 1、直接内存 所有的网络通信和应用程序中(任何语言),每个 TCP Socket 的内核中都有一个发送缓冲区…...
区块链基本理解
文章目录 前言一、什么是分布式账本(DLT)二、什么是P2P网络?二、共识算法三、密码算法前言 区块链是由一个一个数据块组成的链条,按照时间顺序将数据块逐一链接,通过哈希指针链接,所有的数据块共同维护一份分布式账本(DLT),每个节点(可以理解为一个玩家,一台计算机)都拥…...

panda机械臂的正逆运动学分析与仿真
文章目录 前言Panda机械臂的DH参数法建模正运动学逆运动学误差函数雅可比矩阵高斯-牛顿法(Gauss-Newton) 参考代码获取 前言 机械臂的位置运动学分析是机器人控制与轨迹规划的核心基础,其研究内容主要分为正运动学(Forward Kinem…...

QT使用QXlsx读取excel表格中的图片
前言 读取excel表格中的图片的需求比较小众,QXlsx可以操作excel文档,进行图片读取、插入操作,本文主要分享单独提取图片和遍历表格提取文字和图片。 源码下载 github 开发环境准备 把下载的代码中的QXlsx目录,整个拷贝到所创建…...
VulnHub | Breach - 1
🌟 关注这个靶场的其它相关笔记:[网安靶场] 红队综合渗透靶场 —— VulnHub 靶场笔记合集 Breach: 1 ~ VulnHubBreach: 1, made by mrb3n. Download & walkthrough links are available.https://vulnhub.com/entry/breach-1,152/ 0x01:…...
CMake基础及操作笔记
CMake 基础与操作:从入门到精通 前言 CMake 是一个功能强大、跨平台的构建工具,广泛用于 C 项目管理。它通过简洁的配置文件(CMakeLists.txt)描述编译过程,生成适用于不同平台的构建脚本(如 Makefile 或 …...

在Oracle到GreatSQL迁移中排序规则改变引发的乱码问题分析及解决
在Oracle到GreatSQL迁移中排序规则改变引发的乱码问题分析及解决 一、引言 某老系统数据库从 Oracle 迁移至 GreatSQL 过程中,首批迁移(存储过程、表结构、基础数据)顺利完成。然而,第二批数据迁移时出现主键冲突问题࿱…...

开源物联网平台(OpenRemote)
在物联网技术蓬勃发展的当下,OpenRemote作为一款强大的开源物联网平台,正逐渐在多个领域崭露头角。尤其是在智能能源管理领域,它为微电网和分布式能源网络提供了全面且灵活的数据集成与管理方案,展现出独特的优势。 OpenRemote提供…...

JavaScript入门【3】面向对象
1.对象: 1.概述: 在js中除了5中基本类型之外,剩下得都是对象Object类型(引用类型),他们的顶级父类是Object;2.形式: 在js中,对象类型的格式为key-value形式,key表示属性,value表示属性的值3.创建对象的方式: 方式1:通过new关键字创建(不常用) let person new Object();// 添…...

软件安全检测报告:如何全面评估企业级办公软件安全性?
软件安全检测报告对软件的整体安全性进行了全面而细致的评估与呈现,既揭露了软件防范非法入侵的能力,同时也为软件的开发与优化提供了关键性的参考依据。 引言情况 撰写报告旨在明确呈现软件的安全性状态,并为后续的改进工作提供依据。在阐…...
区间dp(竞赛)
一、介绍 区间 dp 其实就是左右端点固定之后,用已经得出的小区间来更新大区间答案的 dp 方式。 非常重要的要素:小区间得到大区间,衍生出来的问题就是对于大区间如何划分成若干小区间,这直接决定了方程的书写。之后的例题中慢慢…...