草庐IT

(十七)最新批量视频换脸、无训练高速换脸、一张图片即可完成、批量处理

李贺~ 2024-04-09 原文

最新批量视频换脸、无训练高速换脸、一张图片即可完成、批量处理

前期发布的视频换脸软件【https://blog.csdn.net/weixin_42398606/article/details/128235374】的效果不是非常好,人脸融合有瑕疵。

本文结合最新的AI模型实现人脸融合,效果得到很好提升。本模型使用多尺度编码器提取原图属性特征,使用预训练人脸识别模型数据提取用户图的ID特征,通过引入可行变特征的融合结构, 将ID特征嵌入其属性特征空间,以光流场的形式实现人脸面部自适应变化,最终使其融合效果真实、融洽、保真,并且支持对目标脸型的自适应感知。
在任意真实人物图像进行直接推理,不用事先训练模型。最好是正脸或偏侧一定小范围角度的侧脸图像,人脸五官轮廓清晰无遮挡,图像分辨率大于128x128,小于3840×2160。

本代码功能是:可以对人物视频进行换脸操作,不用预先耗时训练模型,效率极高;可进行批量视频处理,使用了最新的人工智能的算法。

本文与前几篇博文关联性较强,请事先阅读前几篇。 对此文感兴趣的可以加微深入探讨:herbert156
可运行的试用版本下载:https://pan.baidu.com/s/1JRGaWkj3MzkuBrj6T0idfw
提取码: up3j

如果提示过期,可以向博主索要新的SN文件。

一、主要功能:
以下的Python代码的功能:批量选择视频、批量处理,主要包括:
1、对视频进行换脸操作,并输出变换后的文件;
2、可以批量处理,在选择文件的对话框里可以选择多个文件,进行批量操作;
3、如果电脑有GPU,则会自动选择GPU处理,加快处理速度;
4、信息统计里面可以实时显示处理的各种统计信息;
5、视频处理完毕后自动进行音频的处理与合成。

软件运行界面如下:

二、主要代码:

话不多说,上代码!

UI的Python代码:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'AI_SwapFace1_UI.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_ai_repire(object):
    def setupUi(self, ai_repire):
        ai_repire.setObjectName("ai_repire")
        ai_repire.setEnabled(True)
        ai_repire.resize(912, 681)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        ai_repire.setFont(font)
        ai_repire.setMouseTracking(False)
        self.layoutWidget = QtWidgets.QWidget(ai_repire)
        self.layoutWidget.setGeometry(QtCore.QRect(360, 626, 531, 41))
        self.layoutWidget.setObjectName("layoutWidget")
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.layoutWidget)
        self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.startButton = QtWidgets.QPushButton(self.layoutWidget)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.startButton.setFont(font)
        self.startButton.setObjectName("startButton")
        self.horizontalLayout_5.addWidget(self.startButton)
        self.stopButton = QtWidgets.QPushButton(self.layoutWidget)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.stopButton.setFont(font)
        self.stopButton.setObjectName("stopButton")
        self.horizontalLayout_5.addWidget(self.stopButton)
        spacerItem = QtWidgets.QSpacerItem(60, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_5.addItem(spacerItem)
        self.helpButton = QtWidgets.QPushButton(self.layoutWidget)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.helpButton.setFont(font)
        self.helpButton.setObjectName("helpButton")
        self.horizontalLayout_5.addWidget(self.helpButton)
        self.quitButton = QtWidgets.QPushButton(self.layoutWidget)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.quitButton.setFont(font)
        self.quitButton.setObjectName("quitButton")
        self.horizontalLayout_5.addWidget(self.quitButton)
        self.groupBox_2 = QtWidgets.QGroupBox(ai_repire)
        self.groupBox_2.setGeometry(QtCore.QRect(10, 20, 881, 281))
        self.groupBox_2.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        self.groupBox_2.setAlignment(QtCore.Qt.AlignCenter)
        self.groupBox_2.setObjectName("groupBox_2")
        self.my_label1 = QtWidgets.QLabel(self.groupBox_2)
        self.my_label1.setGeometry(QtCore.QRect(12, 30, 427, 240))
        self.my_label1.setObjectName("my_label1")
        self.my_label2 = QtWidgets.QLabel(self.groupBox_2)
        self.my_label2.setGeometry(QtCore.QRect(443, 30, 427, 240))
        self.my_label2.setObjectName("my_label2")
        self.groupBox_4 = QtWidgets.QGroupBox(ai_repire)
        self.groupBox_4.setGeometry(QtCore.QRect(10, 320, 881, 151))
        self.groupBox_4.setAlignment(QtCore.Qt.AlignCenter)
        self.groupBox_4.setObjectName("groupBox_4")
        self.filesButton = QtWidgets.QPushButton(self.groupBox_4)
        self.filesButton.setGeometry(QtCore.QRect(20, 30, 78, 24))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.filesButton.setFont(font)
        self.filesButton.setObjectName("filesButton")
        self.outButton = QtWidgets.QPushButton(self.groupBox_4)
        self.outButton.setGeometry(QtCore.QRect(20, 108, 78, 24))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.outButton.setFont(font)
        self.outButton.setObjectName("outButton")
        self.txt1 = QtWidgets.QLabel(self.groupBox_4)
        self.txt1.setGeometry(QtCore.QRect(110, 32, 591, 20))
        self.txt1.setObjectName("txt1")
        self.txt2 = QtWidgets.QLabel(self.groupBox_4)
        self.txt2.setGeometry(QtCore.QRect(110, 110, 511, 20))
        self.txt2.setObjectName("txt2")
        self.filesButton1 = QtWidgets.QPushButton(self.groupBox_4)
        self.filesButton1.setGeometry(QtCore.QRect(20, 70, 78, 24))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.filesButton1.setFont(font)
        self.filesButton1.setObjectName("filesButton1")
        self.txt3 = QtWidgets.QLabel(self.groupBox_4)
        self.txt3.setGeometry(QtCore.QRect(110, 70, 581, 20))
        self.txt3.setObjectName("txt3")
        self.my_label3 = QtWidgets.QLabel(self.groupBox_4)
        self.my_label3.setGeometry(QtCore.QRect(650, 20, 213, 120))
        self.my_label3.setObjectName("my_label3")
        self.groupBox_5 = QtWidgets.QGroupBox(ai_repire)
        self.groupBox_5.setGeometry(QtCore.QRect(10, 496, 881, 101))
        self.groupBox_5.setAlignment(QtCore.Qt.AlignCenter)
        self.groupBox_5.setObjectName("groupBox_5")
        self.txt11 = QtWidgets.QLabel(self.groupBox_5)
        self.txt11.setGeometry(QtCore.QRect(20, 30, 861, 16))
        self.txt11.setObjectName("txt11")
        self.txt12 = QtWidgets.QLabel(self.groupBox_5)
        self.txt12.setGeometry(QtCore.QRect(20, 60, 861, 21))
        self.txt12.setObjectName("txt12")
        self.check_result = QtWidgets.QPushButton(ai_repire)
        self.check_result.setGeometry(QtCore.QRect(130, 630, 121, 31))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.check_result.setFont(font)
        self.check_result.setObjectName("check_result")

        self.retranslateUi(ai_repire)
        QtCore.QMetaObject.connectSlotsByName(ai_repire)

    def retranslateUi(self, ai_repire):
        _translate = QtCore.QCoreApplication.translate
        ai_repire.setWindowTitle(_translate("ai_repire", "AI换脸工具"))
        self.startButton.setText(_translate("ai_repire", "开始处理"))
        self.stopButton.setText(_translate("ai_repire", "停止处理"))
        self.helpButton.setText(_translate("ai_repire", "帮助"))
        self.quitButton.setText(_translate("ai_repire", "退出"))
        self.groupBox_2.setTitle(_translate("ai_repire", "预览窗口"))
        self.my_label1.setText(_translate("ai_repire", "原图"))
        self.my_label2.setText(_translate("ai_repire", "输出"))
        self.groupBox_4.setTitle(_translate("ai_repire", "文件设置"))
        self.filesButton.setText(_translate("ai_repire", "换脸视频"))
        self.outButton.setText(_translate("ai_repire", "输出目录"))
        self.txt1.setText(_translate("ai_repire", "请选择被换脸的视频文件[Ctrl+A全选、Ctrl/Shift+鼠标可多选]......"))
        self.txt2.setText(_translate("ai_repire", "换脸完成的视频输出目录"))
        self.filesButton1.setText(_translate("ai_repire", "换脸图片"))
        self.txt3.setText(_translate("ai_repire", "请选择自己的脸部图片"))
        self.my_label3.setText(_translate("ai_repire", "换脸图片"))
        self.groupBox_5.setTitle(_translate("ai_repire", "信息统计"))
        self.txt11.setText(_translate("ai_repire", "【视频信息】"))
        self.txt12.setText(_translate("ai_repire", "【运行信息】"))
        self.check_result.setText(_translate("ai_repire", "查看换脸结果"))

主要核心代码:

#AI视频处理工具_
import os, sys, time, cv2, threading
from PIL import Image,ImageDraw,ImageFont
import moviepy.editor as mpe
import numpy as np

from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QWidget,QMessageBox,QFileDialog,QApplication
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QPixmap, QIcon
from AI_SwapFace1_UI import Ui_ai_repire

os.environ['TORCH_HOME'] = './torch_model'

pil_img = Image.open("start_img.jpg")
ImageDraw.Draw(pil_img).text((160,150), "    AI换脸处理工具", (255,255,255),font=ImageFont.truetype("msyh.ttc", 36))
img_start = cv2.resize(cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR), (427, 240))
ImageDraw.Draw(pil_img).text((410,320), "正在加载AI模型,请稍后 ......", (255,255,255),font=ImageFont.truetype("msyh.ttc", 16))
img_s = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
run_flag = 0
def showpic():  # 以下代码显示软件初始界面
    global ret, frame
    while run_flag == 0:
        cv2.imshow("AI Repire Transfer System", img_s)
        cv2.waitKey(100)
    cv2.destroyAllWindows()
t = threading.Thread(target=showpic)
t.start()

# savedStdout = sys.stdout  #保存标准输出流
file = open('./log.txt', 'wt')  #运行信息到Log文件
sys.stdout = file  #标准输出重定向至文件

DEBUG_FLAG = False
# DEBUG_FLAG = True
os.environ['TORCH_HOME'] = './torch_model'

my_title = "AI换脸工具"
work_path = os.getcwd()
input_path = work_path + '\input'
pic_input_path = work_path + '\input'
out_dir=work_path + '\output'
my_pic_a_path = work_path + '\\input\\face.jpg'
files = [work_path + '\\input\\test.mp4']

filesnums = 1; stop_flag = False; stop_flag_1 = False; t0 = 0; iii = 0

class MainWin(QWidget, Ui_ai_repire):
    def __init__(self):
        super(MainWin, self).__init__()
        self.setupUi(self)
        global hwnd, run_flag
        self.createLayout()
        self.setWindowIcon(QIcon("anime.ico"))
        self.setWindowFlags(Qt.WindowMinimizeButtonHint)
        self.show()
        run_flag = 1
        self.flash_item = True

    def CV2toPIL(self, img):  # cv2转PIL
        return Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA))
    def PILtoCV2(self, img):  # PIL转cv2
        return cv2.cvtColor(np.array(img), cv2.COLOR_RGBA2BGRA)
    def two_pic_combine_PIL(self, back_img, fore_img): #2个图片合并
        back_img = self.CV2toPIL(back_img); fore_img = self.CV2toPIL(fore_img); r,g,b,alpha = fore_img.split()
        return cv2.cvtColor(self.PILtoCV2(Image.composite(fore_img, back_img, alpha)), cv2.COLOR_BGRA2BGR)
    def water_print(self, img):  # 打文字水印
        # cv2.putText(图像,需要添加字符串,需要绘制的坐标,字体类型,字号,字体颜色,字体粗细)
        w = img.shape[1]  # 宽度
        h = img.shape[0]  # 高度
        bk_img = np.zeros((160, 1280, 3), np.uint8)  # Creat a Image
        bk_img = cv2.putText(bk_img, 'wx:herbert156', (80, 130), cv2.LINE_AA, 5, (180, 180, 180), 12)
        bk_img = cv2.resize(bk_img, (w, int(w * 16 / 128)))
        mask = 255 * np.ones(bk_img.shape, bk_img.dtype)
        width, height, channels = img.shape
        center = (height // 2, width // 2)  # 融合的位置,可以自己设置
        res = cv2.seamlessClone(bk_img, img, mask, center, cv2.MONOCHROME_TRANSFER)
        return res

    def RunChange(self, img_path):  #卡通转换功能
        check_face  = self.ulfd_face_detection(img_path)
        if check_face['scores'] == []:
            print("Not found the face......")
            return img_path
        cv2.imwrite('tmp.jpg', img_path)
        template_path = 'tmp.jpg'
        user_path = my_pic_a_path
        result = self.image_face(template_path, user_path).astype(np.uint8)
        # cv2.imwrite('result.png', result[OutputKeys.OUTPUT_IMG])
        # try: os.remove('tmp.jpg')
        # except Exception as errmsg: print(repr(errmsg)); print('删除<tmp.jpg>临时文件发生错误...')
        return result

    def video_change(self, videofile):     #videofile:文件名
        global bg_files, iii, stop_flag, t0
        try: cap = cv2.VideoCapture(videofile)  # 读取视频文件
        except: self.show_error('读取视频文件:'+videofile+'时,出现错误(类型:视频文件无法解码)!'); return
        fps = cap.get(cv2.CAP_PROP_FPS)  # 帧率
        total_fr = cap.get(cv2.CAP_PROP_FRAME_COUNT)  # 总帧数
        size_x = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # 视频流的帧宽度
        size_y = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # 视频流的帧高度
        if fps==0 or total_fr==0 or size_x==0 or size_y==0:
            self.show_error('读取视频文件:' + videofile + '时,出现错误(类型:视频文件格式错误)!'); return
        try: len_m,len_s = divmod(total_fr/fps, 60)
        except: self.show_error('读取视频文件:' + videofile + '时,出现错误(类型:视频文件内容为空)!'); return

        videoinfo = '【文件信息】 视频总数:%d|正在处理(%d/%d):'%(filesnums,iii,filesnums) + os.path.split(videofile)[1]+\
                    '|帧分辨率:%dx%d|视频长度:%d分%d秒|FPS:%.2f帧/秒'%(size_x,size_y,len_m,len_s,fps)
        self.txt11.setText(videoinfo)

        # tempfile = os.path.split(bg_files[0])[0]+'\\out.mp4'
        tempfile = 'out.mp4'
        out = cv2.VideoWriter(tempfile, cv2.VideoWriter_fourcc(*'mp4v'), fps, (size_x, size_y))
        t1 = time.time(); next_fr = 0
        if size_x / size_y > 1.778: fx = 427 / size_x; fy = fx   # 计算16:9的比例,以便缩放不变形
        else: fx = 240 / size_y; fy = fx

        while (True):
            t2 = time.time()
            if stop_flag:
                cap.release();  out.release(); os.remove(tempfile)
                self.txt11.setText('【文件信息】 文件总数:%d个 | 处理完成:%d个' % (filesnums, iii))
                self.txt12.setText('【运行信息】 用户终止了正在进行的转换进程......')
                return
            next_fr += 1
            ret, frame = cap.read()
            if ret:
                if not DEBUG_FLAG: img2 = self.RunChange(frame)
                else: img2 = frame
                #img2 = cv2.cvtColor(frame.copy(), cv2.COLOR_BGR2BGRA)   #测试用语句
                out.write(img2)  # 帧转成视频
            else: break

            self.my_label1.setPixmap(self.CvMatToQImage(cv2.resize(frame,(0,0),fx=fx,fy=fy)))
            self.my_label2.setPixmap(self.CvMatToQImage(cv2.resize(img2, (0, 0), fx=fx, fy=fy)))

            cv2.waitKey(1)
            t3 = time.time(); m1, s1 = divmod(t3-t0, 60); m2, s2 = divmod(t3-t1, 60)
            runinfo = '【运行信息】 总进度:%d%% | 总耗时:%d分%d秒 | 当前耗时:%d分%d秒 | 帧耗时:%.2f秒 | 速度:%.1fFPS'\
                      %(100*next_fr/total_fr, m1, s1, m2, s2,(t3-t2),1/(t3-t2))
            self.txt12.setText(runinfo)

        cap.release(); out.release()
        self.txt12.setText('【运行信息】 正在分离、合成音轨(大概需要:%.1f分钟,不要关闭软件),请稍后......'%(len_m/2))
        cv2.waitKey(1)
        try:
            audio = mpe.AudioFileClip(videofile)  # 分离声轨
            clip = mpe.VideoFileClip(tempfile)
            videoclip = clip.set_audio(audio)  # 写入声轨

            videoclip.write_videofile(out_dir+'/'+os.path.splitext(os.path.split(videofile)[1])[0]+ '_1.mp4',
                                      audio_codec = 'aac')
            # out_file = out_dir + '\\' + os.path.splitext(os.path.split(file)[1])[0] + '_1.mp4'

        except Exception as errmsg: print(repr(errmsg)); print('分离声音发生错误...')
        try: os.remove(tempfile)
        except Exception as errmsg: print(repr(errmsg)); print('删除临时文件发生错误...')
        t3 = time.time()
        self.txt12.setText('【运行信息】 处理完毕!总消耗时间:%d分%d秒'%(m1, s1))
        self.txt11.setText('【文件信息】 文件总数:%d个 | 处理完成:%d个'%(filesnums,iii))


    def CvMatToQImage(self, ptr):  # Converts an opencv MAT format into a QImage
        ptr = cv2.cvtColor(ptr, cv2.COLOR_BGRA2RGBA)  # 颜色格式转换
        QtImg = QtGui.QImage(ptr.data, ptr.shape[1], ptr.shape[0], QtGui.QImage.Format_RGBA8888)
        return QtGui.QPixmap.fromImage(QtImg)

    def show_error(self, str):
        r_button = QMessageBox.question(self, my_title,'\n\n'+str+'\n\n', QMessageBox.Ok)
    # def show_error(self, str):
    #     infoBox = QMessageBox()
    #     infoBox.setIcon(QMessageBox.Information)
    #     infoBox.setText(str)
    #     infoBox.setStandardButtons(QMessageBox.Ok)
    #     infoBox.button(QMessageBox.Ok).animateClick(30000)  # 10秒自动关闭
    #     infoBox.exec_()

    def set_False_Btn(self):
        self.filesButton.setEnabled(False);       self.outButton.setEnabled(False)
        self.startButton.setEnabled(False);       self.stopButton.setEnabled(True)
        self.quitButton.setEnabled(False);        self.filesButton1.setEnabled(False)
    def set_True_Btn(self):
        self.filesButton.setEnabled(True);       self.outButton.setEnabled(True)
        self.startButton.setEnabled(True);       self.stopButton.setEnabled(False)
        self.quitButton.setEnabled(True);        self.filesButton1.setEnabled(True)

    def startrun(self):
        global iii,stop_flag, stop_flag_1, t0
        iii = 0; stop_flag = False
        stop_flag_1 = False
        t0 = time.time()
        if files == []: self.show_error('请选择需要处理的视频文件!'); return
        if my_pic_a_path == '': self.show_error('请选择自己的人像文件!'); return
        if not os.path.exists(out_dir): self.show_error('输出目录不存在,请重新选择!'); return
        self.set_False_Btn()
        self.txt12.setText('【运行信息】 正在初始化AI模型......')

        def run_thread():
            global iii, stop_flag, stop_flag_1, t0
        
            for file in files:
                iii += 1
                if stop_flag: break
                self.video_change(file)
            stop_flag_1 = True

        t = threading.Thread(target=run_thread)
        t.start()

        self.my_timer = QTimer(self)
        self.my_timer.start(500)
        self.my_timer.timeout.connect(self.set_run_over)

    def set_run_over(self):
        if stop_flag_1:
            self.my_timer.stop()
            self.set_True_Btn()
            return

        # if self.txt12.text() == '【运行信息】': self.txt12.setText(self.flash_item_str)
        # else: self.txt12.setText('【运行信息】')

    def stoprun(self):
        global stop_flag
        r_button = QMessageBox.question(self, my_title,
                                        "\n\n    确定要停止视频处理吗?\n\n", QMessageBox.Yes | QMessageBox.No)
        if r_button == QMessageBox.Yes: stop_flag = True

    def helpWin(self):
        str="\n\n\n1、【换脸视频】选择需要处理的视频文件(可多选);\n" \
            "2、【换脸图片】选择自己的脸部图片(单选);\n" + \
            "3、【输出目录】处理后的文件目录,文件名:源文件_*.mp4;\n"+\
            "4、本软件只支持Nvidia系列GPU;\n\n\n"+\
            "      本软件著作权归属:xxx         网址:xxx.com\n\n"
        QMessageBox.question(self, my_title, str, QMessageBox.Ok)
    def quitWin(self):
        r_button = QMessageBox.question(self, my_title,
                                        "\n\n退出将终止处理过程...... \n\n确认退出吗?\n\n", QMessageBox.Yes | QMessageBox.No)
        if r_button == QMessageBox.Yes: sys.exit()

    def checkresult(self):
        os.startfile(out_dir)

    def filesButton_fuc(self):  #选择视频
        global files,filesnums,input_path
        files, ok1 = QFileDialog.getOpenFileNames(self,'请选择视频文件[全选:Ctrl+A、多选:Ctrl/Shift+鼠标]',
                                                       input_path,"*.mp4;*.avi;*.mkv;;*.*")
        filesnums = len(files)
        if files!=[]:
            txt='目录:'+os.path.split(files[0])[0]+'|已选文件:'+str(filesnums)+'个|文件名:'
            for file in files: txt=txt+ os.path.split(file)[1]+'; '
            self.txt1.setText(txt)
            input_path = os.path.dirname(files[0])
        else:
            self.txt1.setText('请选择视频文件[全选:Ctrl+A、多选:Ctrl/Shift+鼠标]......')
    def filesButton1_fuc(self): #选择自己的人脸图片
        global pic_input_path, my_pic_a_path
        my_pic_a_path, _ = QFileDialog.getOpenFileName(self,'请选择脸部图片文件[只能单选]',
                                                       pic_input_path, "*.jpg;*.png;;*.*")
        if my_pic_a_path != '':
            print(my_pic_a_path)
            try:
                img = cv2.imread(my_pic_a_path)
                if img is None: self.show_error('\n图片路径、名称不能包含中文.....\n\n' + '图片名称:' + my_pic_a_path);  return
            except:
                self.show_error('\n图片路径、名称不能包含中文.....\n\n'+'图片名称:'+my_pic_a_path);  return

            size_x = img.shape[1]  # 宽度
            size_y = img.shape[0]  # 高度
            if size_x < 512 or size_y < 512:
                my_pic_a_path = []
                self.show_error('\n请保证图片分辨率大于512x512.....\n\n'+
                                '您选择的图片分辨率:'+str(size_x)+'x'+str(size_y));  return

            self.txt3.setText(my_pic_a_path + ' | 分辨率:'+str(size_x)+'x'+str(size_y))
            pic_input_path = os.path.dirname(my_pic_a_path)
            pix_img = QPixmap(my_pic_a_path)
            pix_img = pix_img.scaled(213, 120, Qt.KeepAspectRatio)
            self.my_label3.setAlignment(Qt.AlignRight)
            self.my_label3.setPixmap(pix_img)
        else:
            self.txt3.setText('请选择脸部图片文件[只能单选]......')
    def outButton_fuc(self):
        global out_dir
        out_dir = QFileDialog.getExistingDirectory(self,'选择转换后的输出文件夹', work_path)
        if out_dir == '': self.txt2.setText('请选择视频变换后的文件保存目录......')
        else: self.txt2.setText(out_dir)

    def createLayout(self):
        self.my_label1.setPixmap(self.CvMatToQImage(img_start))
        self.my_label2.setPixmap(self.CvMatToQImage(img_start))
        pix_img = QPixmap(my_pic_a_path)
        pix_img = pix_img.scaled(213, 120, Qt.KeepAspectRatio)
        self.my_label3.setAlignment(Qt.AlignRight)
        self.my_label3.setPixmap(pix_img)

        self.my_label1.setAlignment(Qt.AlignCenter)
        self.my_label2.setAlignment(Qt.AlignCenter)
        self.my_label1.setFixedSize(427, 240)
        self.my_label2.setFixedSize(427, 240)
        self.my_label1.setAlignment(Qt.AlignCenter)
        self.my_label2.setAlignment(Qt.AlignCenter)
        self.my_label1.setToolTip("本区域,显示的是原始视频缩略图...")
        self.my_label2.setToolTip("本区域,显示的是处理后的缩略图...")
        self.my_label3.setToolTip("本区域,显示的是用户选择的人脸图片...")

        self.filesButton.setToolTip("选择即将被处理的的视频文件,可单选、多选...")
        self.filesButton1.setToolTip("选择请选择自己的脸部图片...")
        self.outButton.setToolTip("选择输出文件目录,处理后的文件将存在此目录...")

        self.txt1.setText(files[0])
        self.txt2.setText(out_dir)
        self.txt3.setText(my_pic_a_path)

        self.filesButton.clicked.connect(self.filesButton_fuc)
        self.filesButton1.clicked.connect(self.filesButton1_fuc)
        self.outButton.clicked.connect(self.outButton_fuc)

        self.stopButton.setEnabled(False)
        self.startButton.clicked.connect(self.startrun)
        self.stopButton.clicked.connect(self.stoprun)
        self.helpButton.clicked.connect(self.helpWin)
        self.quitButton.clicked.connect(self.quitWin)
        self.check_result.clicked.connect(self.checkresult)

#if __name__ == '__main__':
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QtWidgets.QApplication(sys.argv)
MainWin = MainWin()
sys.exit(app.exec_())


有关(十七)最新批量视频换脸、无训练高速换脸、一张图片即可完成、批量处理的更多相关文章

  1. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  2. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  3. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  4. Ruby:行 "m = Hash.new {|h,k| h[k] = []}"完成了什么而 "Hash.new"没有完成? - 2

    一边学习thisRailscast我从Rack中看到了以下源代码:defself.middleware@middleware||=beginm=Hash.new{|h,k|h[k]=[]}m["deployment"].concat[[Rack::ContentLength],[Rack::Chunked],logging_middleware]m["development"].concatm["deployment"]+[[Rack::ShowExceptions],[Rack::Lint]]mendend我的问题是关于第三行。什么是传递block{|h,k|h[k]=[]}到Has

  5. ruby-on-rails - Rails 3,在RAILS_ROOT上方显示来自本地文件系统的jpg图片 - 2

    我正在尝试找出一种方法来显示来自不在RAILS_ROOT下(在RedHat或Ubuntu环境中)的已安装文件系统的图像。我不想使用符号链接(symboliclink),因为这个应用程序实际上是通过Tomcat部署的,而当我关闭Tomcat时,Tomcat会尝试跟随符号链接(symboliclink)并删除挂载中的所有图像。由于这些文件的数量和大小,将图像放在public/images下也不是一种选择。我查看了send_file,但它只会显示一张图片。我需要在一个格式良好的页面中显示6个请求的图像。由于膨胀,我宁愿不使用Base64编码,但我不知道如何将图像数据与呈现的页面一起传递下去。

  6. ruby - 如何批量检查文件内容是否相同 - 2

    我想使用Ruby检查数千对文件中的每对文件是否包含相同的信息。有人能指出我正确的方向吗? 最佳答案 require'fileutils'FileUtils.compare_file('file1','file2')当且仅当文件file1和file2相同时返回true。 关于ruby-如何批量检查文件内容是否相同,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/33769865/

  7. 最新版人脸识别小程序 图片识别 生成二维码签到 地图上选点进行位置签到 计算签到距离 课程会议活动打卡日常考勤 上课签到打卡考勤口令签到 - 2

    技术选型1,前端小程序原生MINA框架cssJavaScriptWxml2,管理后台云开发Cms内容管理系统web网页3,数据后台小程序云开发云函数云开发数据库(基于MongoDB)云存储4,人脸识别算法基于百度智能云实现人脸识别一,用户端效果图预览老规矩我们先来看效果图,如果效果图符合你的需求,就继续往下看,如果不符合你的需求,可以跳过。1-1,登录注册页可以看到登录页有注册入口,注册页如下我们的注册,需要管理员审核,审核通过后才可以正常登录使用小程序1-2,个人中心页登录成功以后,我们会进入个人中心页我们在个人中心页可以注册人脸,因为我们做人脸识别签到,需要先注册人脸才可以进行人脸比对,进

  8. ruby-on-rails - 自动完成搜索的 Rails 实现 - 2

    我不确定如何为我的搜索功能添加自动完成表单。"get"do%>nil%>我有一个具有自定义操作的Controllerdefquery@users=Search.user(params[:query])@article=Search.article(params[:query])end模型如下:defself.user(search)ifsearchUser.find(:all,:conditions=>['first_nameLIKE?',"%#{search}%"])elseUser.find(:all)endenddefself.article(search)ifsearchArt

  9. ruby-on-rails - 使用 Rails 5 完成类(class)和模块分配给用户 - 2

    编辑#2这是类(class)ControllerclassCoursesController编辑#1因此,根据下面Jagdeep的回答,我现在完成了以下操作:类(class).rbclassCoursecourse_modules_user.rbclassCourseModulesUsercourses_user.rbclassCoursesUser用户.rbclassUser迁移classCreateCoursesUsers但是,我遇到这样的错误原始问题所以这是previousquestion的延续,然而,这会偏离那个主题,所以这里是一个新的主题。在此之后,我大致得到了我想要开始工作

  10. ruby - 在 Ruby 中禁用 OptionParser 标志的自动完成 - 2

    #!/usr/bin/envrubyrequire'optparse'options={}OptionParser.newdo|opts|opts.on("--languageLANGUAGE",["Ruby","JavaScript"])do|language|options[:language]=languageendend.parse!puts"Language:#{options[:language]}"如果我用./bin/example--languageRu运行它,它将输出:Language:Ruby我想禁用此自动完成/最接近的匹配行为,并在未提供确切名称时引发Option

随机推荐