在《Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解》节介绍了怎么通过Python+Moviepy+OpenCV实现消除视频Logo的四种方法,并提供了详细的实现思路和实现代码,但这种原生态的应用不适合开发人员以外的其他人员使用,提供一个图形界面的工具程序是比较好的解决方案,本文就介绍实现这样一个图形化工具的步骤。
本节的背景知识都在《Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解》介绍了,在此就不重复介绍了。
本程序复用了《PyQt+moviepy音视频剪辑实战1:多个音视频合成顺序播放或同屏播放的视频文件实现详解》、《PyQt+moviepy音视频剪辑实战1:多视频合成顺序播放或同屏播放的视频文件》的公用框架,该框架提供统一的print输出管理、浮动窗口管理以及系统统一框架。
从上面截图可以看到,主界面处理公共框架的功能外,提供了三大类功能,分别是消除准备(包括选择Logo、选择替换图)、查看功能(包括查看Logo图、查看替换图)、视频Logo消除(包括基于帧的预览、视频预览以及视频输出)。
该窗口可以用于预览图像及信息的展现,使用QGraphicView来实现:
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)
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()
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)
下面是准备消除操作的一个关键槽方法–选择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图像,如果要执行后续操作,请重新选择。")
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
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、输出视频文件截图
使用《PyQt(Python+Qt)学习随笔:windows下使用pyinstaller将PyQt文件打包成exe可执行文件》介绍的方法进行打包。
老猿在win7上最终打包的可执行程序包已经上传到百度云,大家可以下载下来长期免费使用。具体下载地址为百度网盘。
网盘地址