# 將影片之語音轉文字並結合GUI操作 ## 目標 將影片內的語音轉換成文字檔儲存,並使此過程能透過圖形化介面進行操作。 ## 語音轉文字 https://github.com/openai/whisper 直接利用上面現成的工具達成此目標。 ```pip install -U openai-whisper```後 即可使用 ### 程式碼 ```python= import whisper file_path = "D:\starburst\stream\kirito.mp4" model = whisper.load_model("base") result = model.transcribe(file_path) print(result) ``` 檔案類型mp4, mp3試過可以,wav沒試過。 ### 結果 ![](https://hackmd.io/_uploads/HkZd9cjMT.png) 這是result的內容,可以發現顯示了一堆不知道在幹嘛的東西,但那些我們暫時不需要,若只想顯示文字可以改為 ```python print(result["text"]) ``` ![](https://hackmd.io/_uploads/Hkr32qsfa.png) 接著根據後續的需求可能還需要每段文字的開始時間以及結束時間,回去觀察前面第一張的result,發現segments似乎代表每一段文字,裡面的start和end屬性看起來又很像我們要的東西,所以試著再...再...再改一下 ```python for seg in result["segments"]: print(seg["text"], round(seg["start"], 1), round(seg["end"], 1)) ``` round(num, n)用來將num取小數點後第n位 ![](https://hackmd.io/_uploads/Sk8Zgsiz6.png) 舒服多了 ## 儲存文字檔 單純只在螢幕上印出來顯然對後續操作沒有幫助,因此我們要將前面語音轉文字的結果儲存。 ### 程式碼 ```python= import whisper file_path = "D:\starburst\stream\kirito.mp4" model = whisper.load_model("base") result = model.transcribe(file_path) for seg in result["segments"]: buffer = seg["text"] + " " + (str)(round(seg["start"], 1)) + " " + (str)(round(seg["end"], 1)) with open("textAndTime.txt", "a", encoding="UTF-8") as output_file: output_file.write(buffer) output_file.write('\n') with open("textOnly.txt", "a", encoding="UTF-8") as output_file: output_file.write(result["text"]) output_file.write('\n') ``` 這邊我分成兩個檔案儲存,textAndTime有紀錄每一句的開始和結束時間,textOnly則是只有純文字。 ### 結果 ![](https://hackmd.io/_uploads/B1dPNsjM6.png) ## 圖形化介面 每次要換個檔案測試還要手動改file_path好麻煩,於是就找到了名為tkinter的模組,可以用來設計出一個具有圖形使用者介面(Graphical User Interface, GUI)的程式,而且還是Python內建的,不需要用pip安裝,直接import即可。 ### 視窗 以Tk()方法建立一根視窗以放置其他元件 ```python import tkinter as tk root = tk.Tk() root.title("轉換的時刻到了") #視窗標題 root.geometry('700x500') #視窗尺寸 ``` ![](https://hackmd.io/_uploads/rkWttooM6.png) ### 元件 目前只有使用按鈕(Button)與標籤(Label) ```python file_path = "" selectBtn = tk.Button(root, text='選擇檔案', font=('Arial',10,'bold'), # command=selectFile ) confirmBtn = tk.Button(root, text='啟動', font=('Arial',10,'bold'), # command=start ) pathLabel = tk.Label(root, text=file_path, font=('Arial', 10)) #字體與大小 # 位置 selectBtn.pack(anchor=tk.NW) pathLabel.place(x=100, y=0) confirmBtn.pack(anchor=tk.NE) root.mainloop() ``` pack()和place()是用來調整元件在視窗中的位置,不過他有些走位很詭異,看得很矇... 下面連結有詳細說明 http://yhhuang1966.blogspot.com/2018/10/python-gui-tkinter_12.html ![](https://hackmd.io/_uploads/SJGbijsG6.png) pathLabel這個標籤是用來顯示file_path的,但現在file_path="",所以看不到東西。 ### 按鈕事件 ```python from tkinter import filedialog file_path = "" def update(): pathLabel.configure(text=file_path) print("Update path success...") def selectFile(): global file_path file_path = filedialog.askopenfilename() # 選擇檔案後回傳檔案路徑與名稱 print(file_path) # 印出路徑 update() selectBtn = tk.Button(root, text='選擇檔案', font=('Arial',10,'bold'), command=selectFile ) ``` 按鈕被點擊後會呼叫command的函數。 以上面程式碼來說,當selectBtn被點擊後,會呼叫selectFile()來選擇檔案並將路徑紀錄下來,然後進入update()裡利用configure()方法更新pathLabel的內容 ![](https://cdn.discordapp.com/attachments/1073609886430134332/1168140340906172506/Clipchamp_.gif?ex=6550ae78&is=653e3978&hm=a89e49d5c8842209d664ef848a6ab1b50f08a0cdda6a36ff734041cfa1899f55&) 可以看到成功在視窗上顯示剛選取的檔案路徑,雖然這不是重點,重點是file_path會更新成選取的路徑,切換檔案測試時就不用手動改程式碼了。 ## 合體 最後將兩個合併一下就結束這一回合ㄌ ### 程式碼 ```python import tkinter as tk from tkinter import filedialog import whisper root = tk.Tk() root.title("轉換的時刻到了") #視窗標題 root.geometry('700x500') #視窗尺寸 file_path = "" def update(): pathLabel.configure(text=file_path) print("Update path success...") def selectFile(): global file_path file_path = filedialog.askopenfilename() # 選擇檔案後回傳檔案路徑與名稱 print(file_path) # 印出路徑 update() def start(): voiceToText() finish() def voiceToText(): print("Converting...") with open("textAndTime.txt", "w", encoding="UTF-8") as file: file.write("文字+開始時間+結束時間。\n\n") with open("textOnly.txt", "w", encoding="UTF-8") as file: file.write("純文字。\n\n") model = whisper.load_model("base") result = model.transcribe(file_path) # print(result["text"]) for seg in result["segments"]: # print(seg["text"], round(seg["start"], 1), round(seg["end"], 1)) buffer = seg["text"] + " " + (str)(round(seg["start"], 1)) + " " + (str)(round(seg["end"], 1)) with open("textAndTime.txt", "a", encoding="UTF-8") as output_file: output_file.write(buffer) output_file.write('\n') with open("textOnly.txt", "a", encoding="UTF-8") as output_file: output_file.write(result["text"]) output_file.write('\n') print("Text writing completed...") def finish(): finishLabel = tk.Label(root, text="轉換完畢!!", font=('Arial',30)) # 放入標籤,使用 textvariable=text finishLabel.place(x=200, y=200) # Button 設定 command 參數,點擊按鈕時執行函式 selectBtn = tk.Button(root, text='選擇檔案', font=('Arial',10,'bold'), command=selectFile ) confirmBtn = tk.Button(root, text='啟動', font=('Arial',10,'bold'), command=start ) pathLabel = tk.Label(root, text=file_path, font=('Arial', 10)) #字體與大小 # 位置 selectBtn.pack(anchor=tk.NW) pathLabel.place(x=100, y=0) confirmBtn.pack(anchor=tk.NE) root.mainloop() ``` ### 結果 ![](https://cdn.discordapp.com/attachments/1073609886430134332/1168147039276773426/Clipchamp_.gif?ex=6550b4b5&is=653e3fb5&hm=6ead339a47f4bdc68f9ced6e0966cc3101653af45da7df9be37958bf694bfa1e&)