一、引言

在《Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解》节介绍了怎么通过Python+Moviepy+OpenCV实现消除视频Logo的四种方法,并提供了详细的实现思路和实现代码,但这种原生态的应用不适合开发人员以外的其他人员使用,提供一个图形界面的工具程序是比较好的解决方案,本文就介绍实现这样一个图形化工具的步骤。

本节的背景知识都在《Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解》介绍了,在此就不重复介绍了。

二、图形界面设计

本程序复用了《PyQt+moviepy音视频剪辑实战1:多个音视频合成顺序播放或同屏播放的视频文件实现详解》、《PyQt+moviepy音视频剪辑实战1:多视频合成顺序播放或同屏播放的视频文件》的公用框架,该框架提供统一的print输出管理、浮动窗口管理以及系统统一框架。

2.1、主界面

python 中logo 含义 python编程logo_编程语言


从上面截图可以看到,主界面处理公共框架的功能外,提供了三大类功能,分别是消除准备(包括选择Logo、选择替换图)、查看功能(包括查看Logo图、查看替换图)、视频Logo消除(包括基于帧的预览、视频预览以及视频输出)。

2.2、图形化信息预览窗

该窗口可以用于预览图像及信息的展现,使用QGraphicView来实现:

python 中logo 含义 python编程logo_音视频开发_02

三、程序实现

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、初始界面

python 中logo 含义 python编程logo_python_03

2、选择Logo图像的截图

python 中logo 含义 python编程logo_python 中logo 含义_04


3、查看Logo图像的截图

python 中logo 含义 python编程logo_python_05


4、输出视频文件截图

python 中logo 含义 python编程logo_python_06

六、打包成exe

使用《PyQt(Python+Qt)学习随笔:windows下使用pyinstaller将PyQt文件打包成exe可执行文件》介绍的方法进行打包。

老猿在win7上最终打包的可执行程序包已经上传到百度云,大家可以下载下来长期免费使用。具体下载地址为百度网盘。

链接:https://pan.baidu.com/s/1UNaA2UqQBoxx-v8rCIPDhA

提取码:yh2d

选择该链接下的:视频Logo消除工具V2.0.rar

注意:
百度云上分享的《咖啡狗免费工具软件共享空间》下的不同软件安装时必须解压到不同目录,如果解压到同一目录可能有冲突导致不能正常运行,但解压后遵循如下要求可以将其聚合到同一个目录:

  1. 放置到同一目录的不同软件的版本必须相同,版本为压缩文件名中VX.X标注;
  2. 聚合拷贝时除拷贝执行文件外,还有resource目录必须拷贝,如果resource目录下有相同文件名可以覆盖;
  3. 聚合拷贝exe文件和resource目录及其下文件到其他已解压工具目录后,源目录可以删除。