CAFECA-IO / KnowledgeManagement

Creating, Sharing, Using and Managing the knowledge and information of CAFECA
https://mermer.com.tw/knowledge-management
MIT License
0 stars 1 forks source link

(小範圍實驗) 任務 6: 逐字稿生成 #175

Closed TzuHanLiang closed 17 hours ago

TzuHanLiang commented 2 weeks ago

實作目標:

使用現成的工具(如 Google Speech-to-Text)生成音頻的逐字稿。

方法:

選擇適當的逐字稿生成工具(如 Google Speech-to-Text)。 撰寫腳本,將音頻文件上傳到工具並獲取逐字稿。

驗證:

確認可以成功生成逐字稿並保存到本地文件。

TzuHanLiang commented 2 weeks ago

測試

環境有點問題,沒有辦法運行

import os
from pydub import AudioSegment
from pyannote.audio import Pipeline
import whisper
import pandas as pd

# 步驟 1:安裝必要的 Python 庫
# pip install pyannote.audio pydub numpy pandas openai-whisper torch

def detect_silence(audio, silence_thresh=-50.0, chunk_size=10):
    """
    檢測音頻中的無人發言部分
    :param audio: AudioSegment 音頻對象
    :param silence_thresh: 無人發言的閾值(dB)
    :param chunk_size: 分析的片段大小(毫秒)
    :return: 無人發言結束的時間(毫秒)
    """
    trim_ms = 0
    assert chunk_size > 0
    while trim_ms < len(audio):
        if audio[trim_ms:trim_ms + chunk_size].dBFS > silence_thresh:
            return trim_ms
        trim_ms += chunk_size
    return trim_ms

def split_audio(file_path, start_ms, chunk_length_ms=600000):
    """
    分割音頻文件成較小的片段
    :param file_path: 原始音頻文件路徑
    :param start_ms: 開始處理的時間點(毫秒)
    :param chunk_length_ms: 每個片段的長度(毫秒)
    :return: 分割後的音頻片段列表
    """
    audio = AudioSegment.from_file(file_path)
    audio = audio[start_ms:]
    chunks = [audio[i:i + chunk_length_ms] for i in range(0, len(audio), chunk_length_ms)]

    # 保存每個片段
    for i, chunk in enumerate(chunks):
        chunk.export(f"chunk_{i}.wav", format="wav")

    return chunks

def diarize_audio(chunk_index, pipeline):
    """
    對音頻片段進行語者分離
    :param chunk_index: 片段的索引
    :param pipeline: 預訓練語者分離模型
    :return: 語者分離結果
    """
    try:
        diarization = pipeline(f"chunk_{chunk_index}.wav")
        return diarization
    except Exception as e:
        print(f"語者分離失敗,片段索引: {chunk_index}, 錯誤: {e}")
        return None

def transcribe_audio(chunk_index, model):
    """
    對音頻片段進行語音識別
    :param chunk_index: 片段的索引
    :param model: 預訓練語音識別模型
    :return: 語音識別結果
    """
    try:
        result = model.transcribe(f"chunk_{chunk_index}.wav")
        return result["text"]
    except Exception as e:
        print(f"語音識別失敗,片段索引: {chunk_index}, 錯誤: {e}")
        return ""

def process_chunks(chunks, pipeline, model):
    """
    處理每個音頻片段,進行語者分離和語音識別,並合併結果
    :param chunks: 音頻片段列表
    :param pipeline: 預訓練語者分離模型
    :param model: 預訓練語音識別模型
    :return: 包含語者和文字的段落列表
    """
    all_segments = []

    for i, chunk in enumerate(chunks):
        diarization = diarize_audio(i, pipeline)
        transcription = transcribe_audio(i, model)

        if diarization is not None:
            for turn, _, speaker in diarization.itertracks(yield_label=True):
                start_time = turn.start
                end_time = turn.end
                text_segment = transcription  # 這裡可以進一步細分文本段落
                all_segments.append({"start_time": start_time, "end_time": end_time, "speaker_id": speaker, "text": text_segment, "chunk_index": i})

    return all_segments

def save_to_csv(segments, output_file):
    """
    保存結果為 CSV 文件
    :param segments: 包含語者和文字的段落列表
    :param output_file: 輸出的 CSV 文件路徑
    """
    df = pd.DataFrame(segments)
    df.to_csv(output_file, index=False)
    print(f"CSV 文件已生成:{output_file}")

def main():
    """
    主函數:分割音頻、處理每個片段並保存結果為 CSV 文件
    """
    audio_file = "output_audio.wav"  # 替換為你的音頻文件路徑
    output_csv = "transcription.csv"

    # 載入音頻文件
    audio = AudioSegment.from_file(audio_file)

    # 檢測無人發言部分並跳過
    silence_duration = detect_silence(audio)
    print(f"跳過前 {silence_duration / 1000} 秒的無人發言部分")

    # 分割音頻文件
    chunks = split_audio(audio_file, silence_duration)

    # 加載預訓練模型
    pipeline = Pipeline.from_pretrained("pyannote/speaker-diarization")
    model = whisper.load_model("base")

    # 處理每個音頻片段
    segments = process_chunks(chunks, pipeline, model)

    # 保存結果為 CSV 文件
    save_to_csv(segments, output_csv)

if __name__ == "__main__":
    main()

took 3hrs

TzuHanLiang commented 1 day ago