☞ ░ 前往老猿Python博文目錄 ░
一、引言
在《Python音視頻開發:消除抖音短視頻Logo和去電視臺標的實現詳解》節介紹了怎麼通過Python+Moviepy+OpenCV實現消除視頻Logo的四種方法,並提供了詳細的實現思路和實現代碼,但這種原生態的應用不適合開發人員以外的其他人員使用,提供一個圖形界面的工具程序是比較好的解決方案,本文就介紹實現這樣一個圖形化工具的步驟。
本節的背景知識都在《Python音視頻開發:消除抖音短視頻Logo和去電視臺標的實現詳解》介紹了,在此就不重複介紹了。
二、圖形界面設計
本程序複用了《PyQt+moviepy音視頻剪輯實戰1:多個音視頻合成順序播放或同屏播放的視頻文件實現詳解》、《PyQt+moviepy音視頻剪輯實戰1:多視頻合成順序播放或同屏播放的視頻文件》的公用框架,該框架提供統一的print輸出管理、浮動窗口管理以及系統統一框架。
2.1、主界面
從上面截圖可以看到,主界面處理公共框架的功能外,提供了三大類功能,分別是消除準備(包括選擇Logo、選擇替換圖)、查看功能(包括查看Logo圖、查看替換圖)、視頻Logo消除(包括基於幀的預覽、視頻預覽以及視頻輸出)。
2.2、圖形化信息預覽窗
該窗口可以用於預覽圖像及信息的展現,使用QGraphicView來實現:
三、程序實現
3.1、主界面派生類定義及相關初始化方法
class mainWin(QtWidgets.QMainWindow,ui_mainWin.Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.initValues()
self.initSignalAndSlots()
self.initPublicFrame()
def initWidgetSatus(self):#初始化界面元素狀態
self.action_selectReplaceRegion.setEnabled(False)
self.action_selectLog.setEnabled(False)
self.action_previewOneFrame.setEnabled(False)
self.action_viewLogoImg.setEnabled(False)
self.action_videoPreview.setEnabled(False)
self.action_viewReplaceImg.setEnabled(False)
self.action_outputVideo.setEnabled(False)
def initOperations(self):#當選擇不同的視頻文件時要作廢原視頻操作的相關記錄
self.logoSelected = False
self.replaceRegSelected =False
if self.srcFName:
self.videoOperation = CSubVideoImg(self.srcFName)
self.replaceObject = None
self.logoObjList = None
self.destFNameManuChanged = False
def initValues(self):#部分實例變量初始化
self.initWidgetSatus()
self.videoOperation = None
self.srcFName = None
self.destFName = None
self.srcDir = ""
self.destDir = ""
self.showHelpInfo = True #是否顯示操作提示信息
self.destFNameManuChanged = False #輸出文件是否手工修改標記
self.ridLogoManner = ridLogoManner_inpaint #缺省消除方式
self.imgInfW = imgInfoWin.imgInfoWin() #創建圖像信息展示窗
self.fileDialog = QtWidgets.QFileDialog(self)
self.initOperations()
def initPublicFrame(self):#公共框架初始化
self.toggleOperationInfObject = self.actionshowHideOpInf # 顯隱操作信息窗的開關對象如按鈕、動作等,必須可以使用setText方法
self.connectShowInfoSignal = self.actionshowHideOpInf.triggered # 用於觸發打開或關閉輸出信息窗的信號
self.connectAboutSignal = self.actionAbout.triggered # 用於觸發打開about提示窗的信號
self.needShowHints = False # 如果需要在運行窗口顯示初始的操作提示信息,則將此置爲True,並在本類中提供showOperationHints(displayMsgWin)實例方法
def initSignalAndSlots(self): #信號和槽連接,所有重要操作都會重新觸發界面元素狀態設置
self.btn_choiceSrc.clicked.connect(self.chooseFile) #選擇源文件
self.videoFile.textChanged['QString'].connect(self.verifyWidgetStatus)
self.destFile.textEdited.connect(self.verifyWidgetStatus) #目標文件修改了
self.btn_choiceDest.clicked.connect(self.chooseFile)
self.destFile.textEdited.connect(self.manuChangeDestFName) #輸出文件如果手工修改了則不會根據選擇的視頻文件自動同步
self.action_selectLog.triggered.connect(self.selectLog) #選擇Logo區域信號連接
self.action_previewOneFrame.triggered.connect(self.previewOneFrame) #幀預覽信號連接
self.action_videoPreview.triggered.connect(self.videoPreview) #視頻預覽信號連接
self.action_help.triggered.connect(self.help) #幫助信號連接
self.action_selectReplaceRegion.triggered.connect(self.selectReplaceRegion) #替換圖像選擇信號連接
self.action_viewLogoImg.triggered.connect(self.viewLogoImg) #查看Logo圖像信號連接
self.action_viewReplaceImg.triggered.connect(self.viewReplaceImg) #查看替換圖像信號連接
self.action_outputVideo.triggered.connect(self.convertVideo) #輸出處理視頻信號連接
#消除方式變化信號連接
self.radioButton_staticImg.toggled.connect(self.ridLogoMannerSelected)
self.radioButton_frameImg.toggled.connect(self.ridLogoMannerSelected)
self.radioButton_inpaint.toggled.connect(self.ridLogoMannerSelected)
self.radioButton_multiSampleInpaint.toggled.connect(self.ridLogoMannerSelected)
3.2、信息顯示方法
def showHelp(self,helpinf): #顯示幫助信息
print(helpinf)
if self.checkBox_showOperHint.isChecked():
QMessageBox.information(self, '操作提示', helpinf, QMessageBox.Ok )
def showInfo(self,info):#顯示提示信息
print(info)
QMessageBox.information(self, '操作提示', info, QMessageBox.Ok )
def showImgInf(self,*showObjs): #在圖像信息窗按順序顯示相關圖像或文本
imgInfW = self.imgInfW
for showObj in showObjs:
if showObj is None: continue
if isinstance(showObj,str):
imgInfW.showText(showObj)
else:
qtimg = imgInfW.showCVImg(showObj)
imgInfW.show()
3.3、部件狀態設置方法
def verifyWidgetStatus(self): #根據當前操作確認界面元素的狀態
self.initWidgetSatus()
videoFName = self.videoFile.text().strip()
if len(videoFName):
dir = QtCore.QDir("")
if(dir.exists(videoFName)):
self.action_selectLog.setEnabled(True)
if self.ridLogoManner in [ridLogoManner_staticImg,ridLogoManner_frameImg]:
if self.logoSelected:
self.action_selectReplaceRegion.setEnabled(True)
if videoFName!=self.srcFName:
self.srcFName = videoFName
self.initOperations()
if self.logoSelected and (self.replaceRegSelected or self.ridLogoManner in [ridLogoManner_inpaint,ridLogoManner_multiSampleInpaint] ):
self.action_previewOneFrame.setEnabled(True)
self.action_videoPreview.setEnabled(True)
destFName = self.destFile.text().strip()
if len(destFName):
self.action_outputVideo.setEnabled(True)
self.destFName = destFName
if self.replaceRegSelected:
if self.ridLogoManner in [ridLogoManner_staticImg,ridLogoManner_frameImg]:
self.action_viewReplaceImg.setEnabled(True)
if self.logoSelected:
self.action_viewLogoImg.setEnabled(True)
3.4、消除準備相關動作槽方法示例
下面是準備消除操作的一個關鍵槽方法–選擇Logo圖像的槽方法:
def selectLog(self): #實現Logo圖像選擇
fps = int(self.lineEdit_logoSelectFps.text().strip())
if self.ridLogoManner != ridLogoManner_multiSampleInpaint:
helpstr = "將彈出新窗口按設定的幀率播放視頻圖像,請在顯示的視頻上使用鼠標左鍵選擇Logo圖像的範圍。注意:\n" + \
"1、選擇時會有藍色邊框的矩形確認選擇範圍,當選擇完成時鬆開鼠標即可確認選擇;\n" + \
"2、視頻選擇時會停止播放,可以選擇完成後通過鼠標右鍵點擊或鼠標雙擊恢復視頻播放\n" + \
"3、如果選擇錯了可以重新選擇;\n" + \
"4、如果確認選擇結束,按ESC或Q、q三者中的一個退出選擇操作,系統將記錄選擇的Logo圖像;\n" + \
"5、選擇的Logo圖像可以通過查看菜單下的相關菜單進行查看。\n\n" + \
"本提示信息可以通過界面“顯示操作提示信息”複選框關閉。"
else:
helpstr = "將彈出新窗口按設定的幀率播放視頻圖像,請在顯示的視頻上使用鼠標左鍵選擇Logo圖像的範圍。注意:\n" + \
"1、選擇時會有藍色邊框的矩形確認選擇範圍,當選擇完成時鬆開鼠標即可確認選擇;\n" + \
"2、視頻選擇時會停止播放,可以選擇完成後通過鼠標右鍵點擊或鼠標雙擊恢復視頻播放\n" + \
"3、如果選擇錯了可以重新選擇;\n" + \
"4、如果確認選擇,按n、N、s、S將保存當前選擇Logo圖像,恢復播放後可以再選擇Logo再保存,以支持選擇多個Logo圖像" \
"5,按ESC或Q、q三者中的一個退出選擇操作,退出時已選擇圖像會保存,系統將不剔重的記錄選擇的所有Logo圖像;\n" + \
"6、選擇的Logo圖像可以通過查看菜單下的相關菜單進行查看。\n\n" + \
"本提示信息可以通過界面“顯示操作提示信息”複選框關閉。"
self.showHelp(helpstr)
logobjs,frame = self.videoOperation.getROI("select multiLogo Imgs Range",fps)
if logobjs is not None and len(logobjs):
self.logoSelected = True
self.logoObjList = (logobjs, frame)
self.verifyWidgetStatus()
self.frameMask = self.videoOperation.genMultiLogoFrameMask([logobjs[-1]],frame)
self.multiFrameMask = self.videoOperation.genMultiLogoFrameMask(logobjs, frame)
self.frame = frame
else:
self.showInfo("本次操作沒有選擇對應Logo圖像,如果要執行後續操作,請重新選擇。")
3.5、查看動作槽方法示例
def viewLogoImg(self): #查看Logo圖像
if not self.logoSelected:
self.showInfo("當前視頻文件尚未選擇Logo圖像")
return
self.showImgInf( f"截取的Logo共計{len(self.logoObjList[0])}個,除了多采樣圖像修復術外,其他消除方法都只取最後一個。各圖像如下:\n ")
count = 0
logoObjs = self.logoObjList[0]
for logoobj in logoObjs:
self.showImgInf(f" 第{count + 1}個:",logoobj[0])
count += 1
3.6、視頻輸出槽方法
def convertVideo(self): #輸出視頻
self.setEnabled(False)
if self.ridLogoManner in [ridLogoManner_staticImg,ridLogoManner_frameImg]:
ret, inf = self.videoOperation.convertVideo(self.destFName, self.ridLogoManner, self.logoObjList, self.replaceObject)
elif self.ridLogoManner == ridLogoManner_inpaint:
ret,inf = self.videoOperation.convertVideo( self.destFName, self.ridLogoManner, self.logoObjList, frameMask=self.frameMask)
else:
ret,inf = self.videoOperation.convertVideo( self.destFName, self.ridLogoManner, self.logoObjList, frameMask=self.multiFrameMask)
print(inf)
self.setEnabled(True)
四、主程序代碼
if __name__=='__main__':
app = QtWidgets.QApplication(sys.argv)
w = mainWin()
loadWin = loadApp.loadAppWin(w,"視頻Logo消除", True, True)
w.show()
sys.exit(app.exec_())
五、運行截圖
1、初始界面
2、選擇Logo圖像的截圖
3、查看Logo圖像的截圖
4、輸出視頻文件截圖
六、打包成exe
使用《PyQt(Python+Qt)學習隨筆:windows下使用pyinstaller將PyQt文件打包成exe可執行文件》介紹的方法進行打包。
老猿在win7上最終打包的可執行程序包已經上傳到百度雲,大家可以下載下來長期免費使用。具體下載地址爲百度網盤。
鏈接:https://pan.baidu.com/s/1UNaA2UqQBoxx-v8rCIPDhA
提取碼:yh2d
選擇該鏈接下的:視頻Logo消除工具V2.0.rar 即可。
注意:
百度雲上分享的《咖啡狗免費工具軟件共享空間》下的不同軟件安裝時必須解壓到不同目錄,如果解壓到同一目錄可能有衝突導致不能正常運行,但解壓後遵循如下要求可以將其聚合到同一個目錄:
- 放置到同一目錄的不同軟件的版本必須相同,版本爲壓縮文件名中VX.X標註;
- 聚合拷貝時除拷貝執行文件外,還有resource目錄必須拷貝,如果resource目錄下有相同文件名可以覆蓋;
- 聚合拷貝exe文件和resource目錄及其下文件到其他已解壓工具目錄後,源目錄可以刪除。
更多moviepy的介紹請參考《PyQt+moviepy音視頻剪輯實戰文章目錄》或《moviepy音視頻開發專欄》。這2個專欄內容的導讀請參考《Python音視頻剪輯庫MoviePy1.0.3中文教程導覽及可執行工具下載》。
關於老猿的付費專欄
老猿的付費專欄《使用PyQt開發圖形界面Python應用》專門介紹基於Python的PyQt圖形界面開發基礎教程,付費專欄《moviepy音視頻開發專欄》詳細介紹moviepy音視頻剪輯合成處理的類相關方法及使用相關方法進行相關剪輯合成場景的處理,兩個專欄加起來只需要19.9元,都適合有一定Python基礎但無相關專利知識的小白讀者學習。這2個收費專欄都有對應免費專欄,只是收費專欄的文章介紹更具體、內容更深入、案例更多。
付費專欄文章目錄:《moviepy音視頻開發專欄文章目錄》、《使用PyQt開發圖形界面Python應用專欄目錄》。本文對應付費專欄文章爲《Python音視頻開發:消除抖音短視頻Logo的圖形化工具實現過程詳解》。
關於Moviepy音視頻開發的內容,請大家參考《Python音視頻剪輯庫MoviePy1.0.3中文教程導覽及可執行工具下載》的導覽式介紹。
對於缺乏Python基礎的同仁,可以通過老猿的免費專欄《專欄:Python基礎教程目錄》從零開始學習Python。
如果有興趣也願意支持老猿的讀者,歡迎購買付費專欄。