欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python實(shí)現(xiàn)音頻去廣告和字幕提取

 更新時(shí)間:2025年02月06日 09:27:12   作者:敖天羽  
這篇文章主要為大家詳細(xì)介紹了如何使用Python實(shí)現(xiàn)音頻去廣告和字幕提取功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

之前下了一些音頻課,但是存在一些音頻中間插入廣告,更萬(wàn)惡的是,它根本不分是不是整句,只要時(shí)間差不多了就插入。

要去掉廣告我們分為以下步驟依次執(zhí)行:

  • 分析規(guī)律(就是前面找規(guī)律)
  • 廣告提取
  • 識(shí)別廣告
  • 重新拼接

對(duì)于字幕提取,之前其實(shí)我們?cè)?AI 相關(guān)的文章中也介紹過(guò)對(duì)應(yīng)模型,直接轉(zhuǎn)換并處理就可以了,后面再介紹。

分析規(guī)律

和寫(xiě)爬蟲(chóng)一樣,第一點(diǎn)就是要找規(guī)律:用一張草稿紙記錄每個(gè)廣告的起始時(shí)間和結(jié)束時(shí)間,再分析它和整段音頻的關(guān)系。

遺憾的是,在插入時(shí)或許是為了避免裁剪,逐秒計(jì)算(也叫做)后,我得出了一個(gè)結(jié)論:它是在固定時(shí)間(end_time - 3min) + random_offset 值,因?yàn)榱?offset 值的介入,整個(gè)就變的玄學(xué)了起來(lái)。

還好很快我又有了一些新的想法:利用一些識(shí)別的手段把廣告詞裁掉就可以了。

還好廣告詞是固定的,而要處理的音頻卻多,這樣計(jì)算下來(lái) ROI 還是劃算的。

廣告提取

這一步是所有步驟里最耗費(fèi)時(shí)間的,對(duì)于整句來(lái)說(shuō),切割分離是一個(gè)高敏感性的操作,稍微多留白幾百毫秒,你聽(tīng)起來(lái)可能就很難受。只有原始數(shù)據(jù)切割的恰到好處,才能達(dá)到完美還原。

因此我們需要更精細(xì)化,精細(xì)到毫秒的裁剪手段。

Windows 下也不知道用啥,搜了下就選了 Audacity:

以毫秒控制選區(qū),然后切割后如果聽(tīng)感是無(wú)縫的,那么就相當(dāng)于抽離了,如果覺(jué)得怪怪的就再調(diào)整毫秒重新裁,如此反復(fù)直到無(wú)縫銜接。

依賴(lài)列表

下文完整的 import依賴(lài)(因?yàn)閼械迷谖哪┵N完整代碼了):

import glob
import os
from concurrent.futures import ThreadPoolExecutor, as_completed

import numpy as np
import librosa
import torch
import whisper
from pydub import AudioSegment
import soundfile as sf
import torch.nn.functional as F
import shutil

識(shí)別廣告

接下來(lái)我們得到了兩個(gè)片段,一個(gè)是完整版的音頻,另一個(gè)是純廣告音頻,將對(duì)應(yīng)波形的相似度進(jìn)行比對(duì),找到相似的段,再進(jìn)行切割。

當(dāng)然,由于整段二三十分鐘,相對(duì)的來(lái)說(shuō)計(jì)算量會(huì)很大,由于我們知道了總是在一個(gè)音頻快結(jié)束了插入廣告,因此可以先裁剪縮小對(duì)比規(guī)模,然后再進(jìn)行比對(duì),減少計(jì)算量。

其中有一些非常抽象的音頻和數(shù)學(xué)知識(shí),只能說(shuō)謝謝 GPT 老師(我也沒(méi)學(xué)會(huì))

# 已知的廣告片段文件
AD_SNIPPET_FILE  = "./testcase/test2.wav"

# 待處理的音頻文件目錄
audio_dir = "./testcase"
TAIL_SECONDS = 300  # 只截取最后5分鐘處理
SIMILARITY_THRESHOLD = 0.8  # 相似度閾值(0~1之間, 需根據(jù)實(shí)際情況調(diào)整)
SUCCESS_DIR = "./success"
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

def load_audio_segment(file_path, sr=16000, tail_seconds=None):
info = sf.info(file_path)
total_duration = info.duration
if tail_seconds is not None and tail_seconds < total_duration:
    start_time = total_duration - tail_seconds
    audio, _ = librosa.load(file_path, sr=sr, mono=True, offset=start_time, duration=tail_seconds)
    return audio, start_time

else:
    audio, _ = librosa.load(file_path, sr=sr, mono=True)
    return audio, 0.0

def find_audio_snippet(main_audio_path, snippet_audio, snippet_norm, sr=16000, tail_seconds=300):
"""
在 main_audio_path 中尋找 snippet_audio 音頻片段的出現(xiàn)位置。
snippet_audio 為事先加載好的 numpy 數(shù)組,snippet_norm 為 snippet 的二范數(shù),用于相似度計(jì)算。
返回 (ad_start_time, ad_end_time, similarity)
若未找到則返回 (None, None, None)
"""
main_audio, main_start = load_audio_segment(main_audio_path, sr=sr, tail_seconds=tail_seconds)
if len(snippet_audio) > len(main_audio):
    return None, None, None
# 轉(zhuǎn)換到 GPU 張量
main_audio_t = torch.from_numpy(main_audio).float().to(device).unsqueeze(0).unsqueeze(0)  # [1,1,M]
snippet_audio_t = torch.from_numpy(snippet_audio).float().to(device).unsqueeze(0).unsqueeze(0)  # [1,1,S]
# 使用 conv1d 來(lái)進(jìn)行類(lèi)似相似度搜索 (無(wú) snippet 翻轉(zhuǎn))
correlation = F.conv1d(main_audio_t, snippet_audio_t)
correlation = correlation[0, 0].cpu().numpy()
best_index = np.argmax(correlation)
best_value = correlation[best_index]
# 相似度計(jì)算:歸一化
similarity = best_value / snippet_norm if snippet_norm > 0 else 0
ad_start_time = main_start + best_index / sr
ad_end_time = ad_start_time + len(snippet_audio) / s

return ad_start_time, ad_end_time, similarity

重新拼接

找到廣告后我們將廣告段落減去,然后再重新拼接生成新的音頻文件即可。

def process_file(filename, snippet_audio, snippet_norm, sr=16000, tail_seconds=300, similarity_threshold=0.8):
"""
處理單個(gè)文件,找到廣告并移除。
"""
ad_start, ad_end, similarity = find_audio_snippet(filename, snippet_audio, snippet_norm, sr=sr,
                                                  tail_seconds=tail_seconds)

if ad_start is not None and similarity > similarity_threshold:
    # 去除廣告段落
    audio = AudioSegment.from_file(filename)
    part1 = audio[:ad_start * 1000]
    part2 = audio[ad_end * 1000:]
    cleaned = part1 + part2
    cleaned_file = f"output/{os.path.basename(filename)}"
    cleaned.export(cleaned_file, format="mp3")
    shutil.move(filename, SUCCESS_DIR)
    return f"{filename} 已移除廣告,生成 {cleaned_file},相似度:{similarity}"
else:
    return f"{filename} 中未高相似度檢測(cè)到廣告或相似度過(guò)低({similarity})"


def remove_ads():
sr = 16000
# 預(yù)先加載廣告片段
snippet_audio, _ = librosa.load(AD_SNIPPET_FILE , sr=sr, mono=True)
# 計(jì)算snippet的范數(shù),用于相似度歸一化
snippet_norm = np.dot(snippet_audio, snippet_audio)

file_list = [os.path.join(audio_dir, f) for f in os.listdir(audio_dir) if
             f.endswith(".mp3")]

# 使用多線(xiàn)程加速處理
# 線(xiàn)程數(shù)可根據(jù)您的機(jī)器資源調(diào)整
max_workers = 20
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
    futures = {
        executor.submit(process_file, file, snippet_audio, snippet_norm, sr, TAIL_SECONDS,
                        SIMILARITY_THRESHOLD): file
        for file in file_list
    }

    for future in as_completed(futures):
        file = futures[future]
        try:
            res = future.result()
            print(res)
        except Exception as e:
            print(f"{file} 處理時(shí)出錯(cuò): {e}")

字幕提取

下一個(gè)問(wèn)題是,音頻是提取好了,但是音頻的字幕和總結(jié)能力其實(shí)也是一個(gè)亮點(diǎn),這個(gè)也是我們想要有的能力,而好多都是付費(fèi)的,百度網(wǎng)盤(pán)雖然會(huì)員免費(fèi),但是實(shí)際聽(tīng)音頻的過(guò)程中遇到了 Bug,讓我不得不另謀高就。

要使用這個(gè)能力,核心還是使用 whisper這個(gè)模型的能力。

我考慮用 Emby 來(lái)當(dāng)音頻播放器,字幕可以和歌詞字幕一樣,因此就需要生成 lrc 格式的標(biāo)準(zhǔn)文件。

也就是:

  • 提取字幕
  • 給每段字幕和時(shí)間軸進(jìn)行格式轉(zhuǎn)換,轉(zhuǎn)為 lrc 標(biāo)準(zhǔn)格式

而跑 AI 模型的時(shí)候,務(wù)必保證 GPU 加速(否則你會(huì)卡的痛不欲生)。

模型請(qǐng)根據(jù)自己的內(nèi)存和實(shí)際情況決定,不一定是越大越好的,可以先跑一段音頻試試效果。

如果本地沒(méi)有找到對(duì)應(yīng)的模型,whisper 先嘗試下載,也可以使用本地準(zhǔn)備好的模型。

def format_lrc_timestamp(seconds: float) -> str:
"""將秒數(shù)轉(zhuǎn)換為 LRC 格式時(shí)間戳 [mm:ss.xx]"""
total_seconds = int(seconds)
m = total_seconds // 60
s = total_seconds % 60
# 毫秒取兩位小數(shù)
ms = (seconds - total_seconds) * 100
return f"[{m:02d}:{s:02d}.{int(ms):02d}]"

def trans_files():
# 請(qǐng)將此路徑替換為你的音頻目錄路徑
audio_directory = "./audio_files"
# 可根據(jù)需要選擇模型大小,如 "small"、"medium"、"large"
trans_text(audio_directory, model_name="medium", language="zh")

def transcribe_to_lrc(audio_path: str, lrc_path: str, model, language: str = "zh"):
"""
使用已加載的 whisper model 對(duì) audio_path 進(jìn)行轉(zhuǎn)錄,
并將結(jié)果保存為 lrc_path 文件。
"""
result = model.transcribe(audio_path, language=language)
segments = result.get("segments", [])

with open(lrc_path, "w", encoding="utf-8") as f:
    # 可根據(jù)需要添加標(biāo)簽信息,如標(biāo)題、歌手、專(zhuān)輯
    f.write("[ti:未知標(biāo)題]\n")
    f.write("[ar:未知作者]\n")
    f.write("[al:未知專(zhuān)輯]\n\n")

    for seg in segments:
        start_time = format_lrc_timestamp(seg['start'])
        text = seg['text'].strip()
        f.write(f"{start_time}{text}\n")


def trans_text(audio_dir: str, model_name: str = "medium", language: str = "zh"):
# 嘗試使用 GPU
device = "cuda:0" if torch.cuda.is_available() else "cpu"
print(f"使用設(shè)備: {device}")

# 加載模型到指定設(shè)備
# 模型大小可根據(jù)資源調(diào)整,如:tiny, base, small, medium, large
print(f"加載 Whisper 模型 ({model_name}),請(qǐng)稍候...")
model = whisper.load_model(model_name, device=device)
print("模型加載完成。")

# 遍歷指定目錄下所有 mp3
audio_files = glob.glob(os.path.join(audio_dir, "*.mp3"))
if not audio_files:
    print("指定目錄中未找到 MP3 文件。")
    return

for audio_path in audio_files:
    base_name = os.path.splitext(audio_path)[0]
    lrc_path = base_name + ".lrc"

    print(f"處理文件: {audio_path} -> {lrc_path}")
    transcribe_to_lrc(audio_path, lrc_path, model=model, language=language)
    print(f"完成: {lrc_path}")

print("所有文件處理完成!")

def trans_files():
# 請(qǐng)將此路徑替換為你的音頻目錄路徑
audio_directory = "./audio_files"
# 可根據(jù)需要選擇模型大小,如 "small"、"medium"、"large"
trans_text(audio_directory, model_name="medium", language="zh")

目前我還沒(méi)有做總結(jié)功能(主要是普通播放器也沒(méi)地方顯示總結(jié)),但是有了全量文本,相信對(duì)于各位來(lái)說(shuō)并不是難事。

總結(jié)

本文的所有代碼均由 AI 編寫(xiě),可以說(shuō)過(guò)去讓它寫(xiě)的代碼更多的是提效用,我姑且還算一知半解,但是涉及到音頻和數(shù)學(xué)知識(shí)的本功能我是真的一無(wú)所知,但它卻能幫我做出一個(gè)非常完美的效果,真的是科技改變生活了。

以上就是Python實(shí)現(xiàn)音頻去廣告和字幕提取的詳細(xì)內(nèi)容,更多關(guān)于Python音頻去廣告和字幕提取的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • gethostbyaddr在Python3中引發(fā)UnicodeDecodeError

    gethostbyaddr在Python3中引發(fā)UnicodeDecodeError

    本文介紹了gethostbyaddr()在Python?3中引發(fā)UnicodeDecodeError的處理方法,對(duì)大家解決問(wèn)題具有一定的參考價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-05-05
  • Python中逗號(hào)轉(zhuǎn)為空格的三種方法

    Python中逗號(hào)轉(zhuǎn)為空格的三種方法

    本文介紹了Python中將逗號(hào)轉(zhuǎn)換為空格的三種方法,包含使用replace函數(shù)、使用split函數(shù)、使用正則表達(dá)式,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • Python實(shí)現(xiàn)批量合并多個(gè)txt文件并生成Excel文件

    Python實(shí)現(xiàn)批量合并多個(gè)txt文件并生成Excel文件

    在數(shù)據(jù)處理中,有時(shí)會(huì)面臨合并多個(gè)文本文件的任務(wù),本文將詳細(xì)介紹如何使用Python批量合并多個(gè)txt文件,并將其生成為一個(gè)Excel文件,需要的可以參考下
    2023-12-12
  • Python+PyQT5的子線(xiàn)程更新UI界面的實(shí)例

    Python+PyQT5的子線(xiàn)程更新UI界面的實(shí)例

    今天小編就為大家分享一篇Python+PyQT5的子線(xiàn)程更新UI界面的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • Python?Pipeline處理數(shù)據(jù)工作原理探究

    Python?Pipeline處理數(shù)據(jù)工作原理探究

    如果你是一個(gè)Python開(kāi)發(fā)者,你可能聽(tīng)過(guò)"pipeline"這個(gè)術(shù)語(yǔ),但?pipeline?到底是什么,它又有什么用呢?在這篇文章中,我們將探討?Python?中的?pipeline?概念,它們是如何工作的,以及它們?nèi)绾螏椭憔帉?xiě)更清晰、更高效的代碼
    2024-01-01
  • Django values()和value_list()的使用

    Django values()和value_list()的使用

    這篇文章主要介紹了Django values()和value_list()的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-03-03
  • Tensorflow?2.1完成對(duì)MPG回歸預(yù)測(cè)詳解

    Tensorflow?2.1完成對(duì)MPG回歸預(yù)測(cè)詳解

    這篇文章主要為大家介紹了Tensorflow?2.1完成對(duì)MPG回歸預(yù)測(cè)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • python3使用urllib模塊制作網(wǎng)絡(luò)爬蟲(chóng)

    python3使用urllib模塊制作網(wǎng)絡(luò)爬蟲(chóng)

    本文給大家介紹的是利用urllib模塊通過(guò)指定的URL抓取網(wǎng)頁(yè)內(nèi)容 所謂網(wǎng)頁(yè)抓取,就是把URL地址中指定的網(wǎng)絡(luò)資源從網(wǎng)絡(luò)流中讀取出來(lái),保存到本地,有需要的小伙伴可以參考下
    2016-04-04
  • Python中字符串List按照長(zhǎng)度排序

    Python中字符串List按照長(zhǎng)度排序

    這篇文章主要介紹了字符串List按照長(zhǎng)度排序(python)的實(shí)現(xiàn)方法啊,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-07-07
  • python?open函數(shù)中newline參數(shù)實(shí)例詳解

    python?open函數(shù)中newline參數(shù)實(shí)例詳解

    newLine()方法可用于輸出一個(gè)換行字符"/n",下面這篇文章主要給大家介紹了關(guān)于python?open函數(shù)中newline參數(shù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06

最新評(píng)論