【原創】基于視覺大模型gemma-3-4b實現短視頻自動識別內容并生成解說文案


📦 一、整體功能定位

這是一個用于從原始視頻自動生成短視頻解說內容的自動化工具,包含:

  1. 視頻抽幀(可基于畫面變化提取關鍵幀)

  2. 多模態圖像識別(每幀圖片理解)

  3. 文案生成(大模型生成口語化解說)

  4. TTS語音合成(將文案變為解說音頻)

  5. 視頻合成(圖片 + 音頻 or 原視頻音頻)

  6. 日志記錄 + 文案保存

適合用于內容創作、短視頻自動剪輯輔助、圖文轉視頻等場景。


🧱 二、代碼架構總覽

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)  # 請取消注釋后運行

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81489.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81489.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81489.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

每日算法刷題計劃Day5 5.13:leetcode數組3道題,用時1h

11. 26. 刪除有序數組中的重復項(簡單&#xff0c;雙指針) 26. 刪除有序數組中的重復項 - 力扣&#xff08;LeetCode&#xff09; 思想: 1.我的思想: 雙指針遍歷集合儲存已有元素 2.官方思想&#xff1a; 題目條件有序數組刪除重復元素&#xff0c;所以重復元素都是連續存在…

Transformer 架構在目標檢測中的應用:YOLO 系列模型解析

目錄 Transformer 架構在目標檢測中的應用&#xff1a;YOLO 系列模型解析 一、YOLO 模型概述 二、YOLO 模型的核心架構 &#xff08;一&#xff09;主干網絡 &#xff08;二&#xff09;頸部結構 &#xff08;三&#xff09;頭部結構 三、YOLO 模型的工作原理 &#xf…

一個完整的項目示例:taro開發微信小程序

前一周完成了一個項目&#xff0c;體測成績轉換的工具&#xff0c;沒做記錄&#xff0c;。這次計劃開發一個地圖應用小程序&#xff0c;記錄一下。方便給使用的人。 一、申請微信小程序&#xff0c;填寫相應的信息&#xff0c;取得開發者ID。這個要給騰訊地圖使用的。 二、申…

動態規劃-LCR 166.珠寶的最大價值-力扣(LeetCode)

一、題目解析 frame二維矩陣中每個值代表珠寶的價值&#xff0c;現在從左上角開始拿珠寶&#xff0c;只能向右或向下拿珠寶&#xff0c;到達右下角時停止拿珠寶&#xff0c;要求拿的珠寶價值最大。 二、算法解析 1.狀態表示 我們想要知道的是到達[i,j]為位置時的最大價值&am…

安裝nerdctl和buildkitd腳本命令

#!/bin/bash set -euo pipefail # 檢查是否以root權限運行 if [ "$(id -u)" -ne 0 ]; then echo "錯誤&#xff1a;請使用root權限或sudo運行本腳本" >&2 exit 1 fi # 檢測openEuler系統&#xff08;兼容大小寫&#xff09; detect_distrib…

實現視頻分片上傳 OSS

訪問 OSS 有兩種方式&#xff0c;本文用到的是使用臨時訪問憑證上傳到 OSS&#xff0c;不同語言版本的代碼參考&#xff1a; 使用STS臨時訪問憑證訪問OSS_對象存儲(OSS)-阿里云幫助中心 1.安裝并使用 首先我們要安裝 OSS&#xff1a; npm install ali-oss --save 接著我們…

動態規劃(3)學習方法論:構建思維模型

引言 動態規劃是算法領域中一個強大而優雅的解題方法,但對于許多學習者來說,它也是最難以掌握的算法范式之一。與貪心算法或分治法等直觀的算法相比,動態規劃往往需要更抽象的思維和更系統的學習方法。在前兩篇文章中,我們介紹了動態規劃的基礎概念、原理以及問題建模與狀…

elementplus el-tree 二次封裝支持配置刪除后展示展開或折疊編輯復選框懶加載功能

本文介紹了基于 ElementPlus 的 el-tree 組件進行二次封裝的 TreeView 組件&#xff0c;使用 Vue3 和 JavaScript 實現。TreeView 組件通過 props 接收樹形數據、配置項等&#xff0c;支持懶加載、節點展開/收起、節點點擊、刪除、編輯等操作。組件內部通過 ref 管理樹實例&…

2025年滲透測試面試題總結-安恒[實習]安全工程師(題目+回答)

網絡安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 安恒[實習]安全工程師 一面 1. 自我介紹 2. 前兩段實習做了些什么 3. 中等難度的算法題 4. Java的C…

網絡編程中的直接內存與零拷貝

本篇文章會介紹 JDK 與 Linux 網絡編程中的直接內存與零拷貝的相關知識&#xff0c;最后還會介紹一下 Linux 系統與 JDK 對網絡通信的實現。 1、直接內存 所有的網絡通信和應用程序中&#xff08;任何語言&#xff09;&#xff0c;每個 TCP Socket 的內核中都有一個發送緩沖區…

TransmittableThreadLocal使用場景

&#x1f680; 為什么要用 TransmittableThreadLocal&#xff1f;一文讀懂線程上下文傳遞問題 在 Java Web 開發中&#xff0c;我們經常用 ThreadLocal 來保存每個請求的用戶信息&#xff0c;例如 userId。但當我們使用線程池或異步方法&#xff08;如 Async&#xff09;時&am…

Milvus(24):全文搜索、文本匹配

1 全文搜索 全文搜索是一種在文本數據集中檢索包含特定術語或短語的文檔&#xff0c;然后根據相關性對結果進行排序的功能。該功能克服了語義搜索的局限性&#xff08;語義搜索可能會忽略精確的術語&#xff09;&#xff0c;確保您獲得最準確且與上下文最相關的結果。此外&…

2000 元以下罕見的真三色光源投影儀:雷克賽恩Cyber Pro1重新定義入門級投影體驗

當性價比遇上技術瓶頸 在 2000元以下的1080P投影儀&#xff0c;單LCD 技術長期主導。而三色光源的DLP和3LCD真1080P都在4000元以上。 單LCD投影為純白光光源&#xff0c;依賴CF濾光膜導致光效低下&#xff0c;普遍存在" 色彩失真 " 等問題。數據顯示&#xff0c;該價…

Maven 下載安裝與配置教程

## 1. Maven 簡介 Maven 是一個項目管理和構建自動化工具&#xff0c;主要用于 Java 項目。Maven 可以幫助開發者管理項目的構建、報告和文檔&#xff0c;簡化項目依賴管理。 ## 2. 下載 Maven 1. 訪問 Maven 官方網站 [https://maven.apache.org/download.cgi](https://maven.…

C# 深入理解類(從類的外部訪問靜態成員)

從類的外部訪問靜態成員 在前一章中&#xff0c;我們看到使用點運算符可以從類的外部訪問public實例成員。點運算符由實 例名、點和成員名組成。 就像實例成員&#xff0c;靜態成員也可以使用點運算符從類的外部訪問。但因為沒有實例&#xff0c;所以最常 用的訪問靜態成員的方…

Java在微服務架構中的最佳實踐:從設計到部署

在2025年的云計算和分布式系統時代&#xff0c;微服務架構已成為構建高可擴展、高可用系統的標準方法&#xff0c;廣泛應用于電商、金融和物聯網等領域。Java憑借其成熟的生態系統、強大的并發支持和跨平臺能力&#xff0c;是微服務開發的首選語言。例如&#xff0c;我們的訂單…

文件讀取漏洞路徑與防御總結

文件讀取漏洞路徑與防御總結 文件讀取漏洞允許攻擊者通過路徑遍歷等手段訪問未授權的文件。以下是Linux和Windows系統中常見敏感路徑的歸納及防御建議&#xff1a; Linux 系統常見敏感路徑 系統關鍵文件&#xff1a; /etc/passwd&#xff1a;用戶賬戶信息&#xff08;可被用來…

react-router基本寫法

1. 創建項目并安裝所有依賴 npx create-react-app react-router-pro npm i 2. 安裝所有的 react router 包 npm i react-router-dom 3. 啟動項目 npm run start router/index.js // 創建路由實例 綁定path elementimport Layout from "/pages/Layout"; import…

uni-app 開發HarmonyOS的鴻蒙影視項目分享:從實戰案例到開源后臺

最近&#xff0c;HBuilderX 新版本發布&#xff0c;帶來了令人興奮的消息——uni-app 現在支持 Harmony Next 平臺的 App 開發。這對于開發者來說無疑是一個巨大的福音&#xff0c;意味著使用熟悉的 Vue 3 語法和開發框架&#xff0c;就可以為鴻蒙生態貢獻自己的力量。 前言 作…

純css實現蜂窩效果

<!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>蜂窩效果</title><style>body {margin: 0…