Python音视频开发:实现消除抖音短视频Logo的工具

Karen110
• 阅读 1280

一、引言

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

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

二、图形界面设计

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

2.1、主界面

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

2.2、图形化信息预览窗

该窗口可以用于预览图像及信息的展现,使用QGraphicView来实现:
Python音视频开发:实现消除抖音短视频Logo的工具

三、程序实现

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的工具

2、选择Logo图像的截图
Python音视频开发:实现消除抖音短视频Logo的工具
3、查看Logo图像的截图
Python音视频开发:实现消除抖音短视频Logo的工具
4、输出视频文件截图
Python音视频开发:实现消除抖音短视频Logo的工具

六、打包成exe

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

老猿在win7上最终打包的可执行程序包已经上传到百度云,大家可以下载下来长期免费使用。由于头条号、公众号、百家号对外网链接的限制,具体下载地址大家通过阅读原文获取。

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

  1. 放置到同一目录的不同软件的版本必须相同,版本为压缩文件名中VX.X标注;

  2. 聚合拷贝时除拷贝执行文件外,还有resource目录必须拷贝,如果resource目录下有相同文件名可以覆盖;

  3. 聚合拷贝exe文件和resource目录及其下文件到其他已解压工具目录后,源目录可以删除。

更多moviepy的介绍请参考《PyQt+moviepy音视频剪辑实战文章目录》或《moviepy音视频开发专栏》。这2个专栏内容的导读请参考《Python音视频剪辑库MoviePy1.0.3中文教程导览及可执行工具下载》。

关于本文的原文链接:

本文在CSDN的“老猿Python”首发,头条号、微信公众号和百家号“老猿Python”转载,如果您是在头条看到本文,请点击文章底部最下方的“了解更多”跳转CSDN阅读原文,如果是微信公众号看到本文,请点击文章底部最下方的“阅读原文”跳转CSDN阅读原文,否则请在百度搜索或CSDN搜索中输入"CSDN老猿Python”加文章标题关键字搜寻本文。

关于老猿的付费专栏

老猿的付费专栏《使用PyQt开发图形界面Python应用》专门介绍基于Python的PyQt图形界面开发基础教程,付费专栏《moviepy音视频开发专栏》详细介绍moviepy音视频剪辑合成处理的类相关方法及使用相关方法进行相关剪辑合成场景的处理,两个专栏加起来只需要19.9元,都适合有一定Python基础但无相关专利知识的小白读者学习。这2个收费专栏都有对应免费专栏,只是收费专栏的文章介绍更具体、内容更深入、案例更多。

付费专栏文章目录:《moviepy音视频开发专栏文章目录》、《使用PyQt开发图形界面Python应用专栏目录》。本文对应付费专栏文章为《Python音视频开发:消除抖音短视频Logo的图形化工具实现过程详解》。

关于Moviepy音视频开发的内容,请大家参考《Python音视频剪辑库MoviePy1.0.3中文教程导览及可执行工具下载》的导览式介绍。

对于缺乏Python基础的同仁,可以通过老猿的免费专栏《专栏:Python基础教程目录》从零开始学习Python。

如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。

Python音视频开发:实现消除抖音短视频Logo的工具

跟老猿学Python!

本文转转自微信公众号老猿Python原创https://mp.weixin.qq.com/s/-dr4Zv68uX5ZNME-wHW1Vw,如有侵权,请联系删除。

点赞
收藏
评论区
推荐文章
浅梦一笑 浅梦一笑
4个月前
初学 Python 需要安装哪些软件?超级实用,小白必看!
编程这个东西是真的奇妙。对于懂得的人来说,会觉得这个工具是多么的好用、有趣,而对于小白来说,就如同大山一样。其实这个都可以理解,大家都是这样过来的。那么接下来就说一下python相关的东西吧,并说一下我对编程的理解。本人也是小白一名,如有不对的地方,还请各位大神指出01名词解释:如果在编程方面接触的比较少,那么对于软件这一块,有几个名词一定要了解,比如开发环
Jacquelyn38 Jacquelyn38
1年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
小森森 小森森
4个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
Stella981 Stella981
1年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
1年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
1年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
1年前
MySQL查询按照指定规则排序
1.按照指定(单个)字段排序selectfromtable_nameorderiddesc;2.按照指定(多个)字段排序selectfromtable_nameorderiddesc,statusdesc;3.按照指定字段和规则排序selec
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
helloworld_34035044 helloworld_34035044
6个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
京东云开发者 京东云开发者
2个月前
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究