本文与前几篇博文关联性较强,请事先阅读前几篇。 对此文感兴趣的可以加微深入探讨:herbert156
软件打包上传了百度网盘:
【视频背景移除】链接: https://pan.baidu.com/s/1Dhr7MHkGd8vlXfyETrK3xQ 提取码: 29n2
【图像背景移除】链接: https://pan.baidu.com/s/1nhuWMYWvTV3B0d0CdGDhFQ 提取码: q46x
【摄像头背景移除】链接: https://pan.baidu.com/s/1tx88C3dCH3ej3PJcKLDmdQ 提取码: g7xy
一、图片抠图
代码主要功能是:图像背景移除、去背景、人像抠图、换背景等,主要包括:
1、对人像图片进行抠图,可以存成PNG格式的透明图片,也可选择替换背景(可选择替换成纯色的、也可选替换成指定背景图片);
2、可以批量抠图,在选择文件的对话框里可以选择多个文件,同时抠图、批量操作;
3、软件设置里可以设置使用GPU处理还是CPU处理,软件只支持英伟达(Nvidia)的GTX/RTX系列的GPU;
4、抠图算法的模型有复杂模型和简易模型,可以根据原始图片的背景复杂度随意选择。
二、图片抠图代码
def image_change_background(self, imagefile, backfile):
global bg_file, iii, stop_flag, t0
#try: img2= cv2.imread(imagefile) # 读取文件
try:
img2= cv2.imdecode(np.fromfile(imagefile, dtype=np.uint8), -1)
if img2.shape[2] == 4: img2 = cv2.cvtColor(img2, cv2.COLOR_BGRA2BGR)
except: self.show_error('读取图片文件【'+imagefile+'】时,出现错误!\n\n原因:目录/文件名不能包含中文......'); return
if img2 is None: self.show_error('读取图片文件【'+imagefile+'】时,出现错误!\n\n原因:目录/文件名不能包含中文......'); return
size_x = img2.shape[1] # 宽度
size_y = img2.shape[0] # 高度
imageinfo = '【图片信息】 文件总数:%d | 正在处理(%d/%d):'%(filesnums,iii,filesnums) + os.path.split(imagefile)[1]+\
' | 图像分辨率:%dx%d '%(size_x,size_y)
self.txt11.setText(imageinfo)
img1 = cv2.resize(backfile, (size_x, size_y))
t1 = time.time()
if size_x / size_y > 1.778: fx = 427 / size_x; fy = fx # 计算16:9的比例,以便缩放不变形
else: fx = 240 / size_y; fy = fx
if stop_flag:
self.txt11.setText('【视频信息】 文件总数:%d个 | 处理完成:%d个' % (filesnums, iii))
self.txt12.setText('【运行信息】 用户终止了正在运行的程序......')
return
img3 = self.koutu(img2)
#img3 = cv2.cvtColor(img2.copy(), cv2.COLOR_BGR2BGRA) #测试用语句
png_name = out_dir+'/'+os.path.splitext(os.path.split(imagefile)[1])[0]+'_1.png'
#cv2.imwrite(png_name,img3)
cv2.imencode('.png', img3)[1].tofile(png_name)
if not only_png:
img4 = self.two_pic_combine_PIL(img1,img3) #img1:背景文件;img2:源文件;img3:抠图文件;img4:合并背景文件
if Box2_flag: self.my_label2.setPixmap(self.CvMatToQImage(cv2.resize(img4, (0, 0), fx=fx, fy=fy)))
jpg_name = out_dir+'/'+os.path.splitext(os.path.split(imagefile)[1])[0]+'_1.jpg'
#cv2.imwrite(jpg_name,img4)
cv2.imencode('.jpg', img4)[1].tofile(jpg_name)
else:
if Box2_flag: self.my_label2.setPixmap(self.CvMatToQImage(cv2.resize(img3,(0,0),fx=fx,fy=fy)))
if Box1_flag: self.my_label1.setPixmap(self.CvMatToQImage(cv2.resize(img2,(0,0),fx=fx,fy=fy)))
cv2.waitKey(1); t2 = time.time()
runinfo = '【运行信息】 当前图片处理耗时:%.3f秒 | 总处理耗时:%.1f秒 | 处理进度:%.1f%%'%((t2-t1),(t2-t0),100*(iii/filesnums))
self.txt12.setText(runinfo); cv2.waitKey(1)
self.txt12.setText('【运行信息】 处理完毕!总消耗时间:%d秒'%(t2-t0))
self.txt11.setText('【视频信息】 文件总数:%d个 | 处理完成:%d个'%(filesnums,iii))
在这里插入代码片
三、界面操作代码
def show_error(self,str):
r_button = QMessageBox.question(self, my_title,'\n\n'+str+'\n\n', QMessageBox.Ok)
def set_False_Btn(self):
self.filesButton.setEnabled(False); self.outButton.setEnabled(False)
#self.bkfileButton.setEnabled(False); #self.checkBox3.setEnabled(False)
self.startButton.setEnabled(False); self.stopButton.setEnabled(True)
self.quitButton.setEnabled(False); #self.checkBox3_0.setEnabled(False)
def set_True_Btn(self):
self.filesButton.setEnabled(True); self.outButton.setEnabled(True)
#self.bkfileButton.setEnabled(True); #self.checkBox3.setEnabled(True)
self.startButton.setEnabled(True); self.stopButton.setEnabled(False)
self.quitButton.setEnabled(True); #self.checkBox3_0.setEnabled(True)
def startrun(self):
global iii,stop_flag,t0
iii = 0; stop_flag = False
self.txt12.setText('【运行信息】 正在初始化AI模型......');cv2.waitKey(1)
t0 = time.time()
if files == []: self.show_error('请选择需要抠图或替换背景的图像文件!'); return
if not os.path.exists(out_dir): self.show_error('输出目录不存在,请重新选择!'); return
self.set_False_Btn()
if not Box3_flag and not only_png:
#try: back_ground = cv2.imread(bg_file) # 读取背景文件
try: back_ground = cv2.imdecode(np.fromfile(bg_file, dtype=np.uint8), -1)
except: self.show_error('读取背景文件时,出现错误!'); self.set_True_Btn(); return
if back_ground is None: self.show_error('读取背景文件时,出现错误!\n\n原因:文件不存在或文件名包含中文...... '); self.set_True_Btn(); return
else: back_ground = bk_img
for file in files:
iii += 1
if stop_flag: break
self.image_change_background(file, back_ground)
self.set_True_Btn()
#self.my_label1.setPixmap(QPixmap("ican_logo.png"))
#self.my_label2.setPixmap(QPixmap("ican_logo.png"))
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、【选择文件】选择需要抠图、替换背景的图像文件;\n2、【输出目录】替换后的文件目录,文件名:源文件_1.mp4;\n"+\
"3、【背景文件】选择后,人物图像的背景都被替换成此背景;\n4、【纯色背景】点选后,所有图片背景替换成纯色的;\n"+ \
"5、可以选择只输出PNG透明文件或同时输出背景替换文件;\n" + \
"6、如没有Nvidia系列GPU,就选CPU处理,AI需选【简易】;\n7、AI抠图算法有简易、复杂2种,可在软件设置栏目里面选择;\n\n\n"+\
" 本软件著作权归属:XXX 网址:www.???.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 filesButton_fuc(self):
global files,filesnums
files, ok1 = QFileDialog.getOpenFileNames(self,'请选择图像文件[全选:Ctrl+A、多选:Ctrl/Shift+鼠标]',
input_path,"*.jpg;;*.png")
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)
else: self.txt1.setText('请选择图像文件[全选:Ctrl+A、多选:Ctrl/Shift+鼠标]......')
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 bkfileButton_fuc(self):
global bg_file
bg_file, ok1 = QFileDialog.getOpenFileName(self,"选择背景图片文件",work_path,"*.jpg;;*.png;;*.gif")
if bg_file == '': self.txt3.setText('请选择背景图片文件......')
else: self.txt3.setText(bg_file)
def click_comboBox1(self, text):
global if_use_gpu,if_good_model
if text == 'GPU':
if GPU_memsize < 3 and self.comboBox2.currentIndex() == 1:
self.show_error("\nGPU名称:%s\nGPU内存:%dG\n\n注意:\nGPU内存必须大于3G才能进行复杂AI模型,\n请选择简易模型!"
%(GPU_name,GPU_memsize))
self.comboBox2.setCurrentIndex(0);if_good_model = False
if_use_gpu = True
else: if_use_gpu = False
def click_comboBox2(self, text):
global if_good_model,if_use_gpu
if text == '复杂':
if GPU_memsize < 3 and self.comboBox1.currentIndex() == 1:
self.show_error("\nGPU名称:%s\nGPU内存:%dG\n\n注意:\nGPU内存必须大于3G才能进行复杂AI模型,\n请选择简易模型或CPU处理!"
% (GPU_name, GPU_memsize))
self.comboBox1.setCurrentIndex(0);if_use_gpu = False
if_good_model = True
else: if_good_model = False
def box_choose(self):
global Box1_flag, Box2_flag
if self.checkBox1.isChecked(): Box1_flag = True
else:Box1_flag = False; self.my_label1.setPixmap(QPixmap("start_img.jpg"))
if self.checkBox2.isChecked(): Box2_flag = True
else:Box2_flag = False; self.my_label2.setPixmap(QPixmap("start_img.jpg"))
def box_choose3_0(self):
global only_png
if self.checkBox3_0.isChecked():
self.txt3.setEnabled(False); self.bkfileButton.setEnabled(False)
self.checkBox3.setEnabled(False)
self.txt3.setText('只输出PNG透明文件,不更换背景......')
only_png = True
else:
self.txt3.setEnabled(True); self.bkfileButton.setEnabled(True)
self.txt3.setText(bg_file); self.checkBox3.setEnabled(True)
only_png = False
def box_choose3(self):
global Box3_flag
if self.checkBox3.isChecked():
self.txt3.setEnabled(False); self.bkfileButton.setEnabled(False)
self.txt3.setText('已经选择纯色背景......')
self.set_rgb_True()
Box3_flag = True
else:
self.txt3.setEnabled(True); self.bkfileButton.setEnabled(True)
self.txt3.setText(bg_file)
self.set_rgb_False()
Box3_flag = False
四、界面布局代码
def createLayout(self):
mainLayout = QtWidgets.QVBoxLayout();topLayout1 = QtWidgets.QHBoxLayout();topLayout2 = QtWidgets.QHBoxLayout()
topLayout3 = QtWidgets.QHBoxLayout();topLayout4 = QtWidgets.QHBoxLayout()
self.my_label1 = QtWidgets.QLabel(); self.my_label2 = QtWidgets.QLabel()
topLayout1.addWidget(self.my_label1); topLayout1.addWidget(self.my_label2)
self.my_label1.setPixmap(QPixmap("start_img.jpg")); self.my_label2.setPixmap(QPixmap("start_img.jpg"))
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.GroupBox1 = QtWidgets.QGroupBox("软件设置")
self.GroupBox1.setFixedSize(280, 60)
self.lbl_1 = QtWidgets.QLabel("处理器:", self)
self.lbl_1.setFixedSize(45, 25)
self.comboBox1 = QtWidgets.QComboBox(self)
self.comboBox1.setFixedSize(50, 25)
self.comboBox1.addItem("CPU"); self.comboBox1.addItem("GPU")
if if_use_gpu: self.comboBox1.setCurrentIndex(1)
self.comboBox1.activated[str].connect(self.click_comboBox1)
self.lbl_2 = QtWidgets.QLabel("AI算法:", self)
self.lbl_2.setFixedSize(45, 25)
self.comboBox2 = QtWidgets.QComboBox(self)
self.comboBox2.setFixedSize(50, 25)
self.comboBox2.addItem("简易"); self.comboBox2.addItem("复杂")
if if_good_model:self.comboBox2.setCurrentIndex(1)
self.comboBox2.activated[str].connect(self.click_comboBox2)
GroupBox1Layout = QtWidgets.QHBoxLayout()
GroupBox1Layout.addWidget(self.lbl_2)
GroupBox1Layout.addWidget(self.comboBox2)
GroupBox1Layout.addWidget(self.lbl_1)
GroupBox1Layout.addWidget(self.comboBox1)
self.GroupBox1.setLayout(GroupBox1Layout)
if not if_use_gpu: self.comboBox1.setEnabled(False)
self.GroupBox2 = QtWidgets.QGroupBox("显示设置")
self.GroupBox2.setFixedSize(180, 60)
self.checkBox1 = QtWidgets.QCheckBox("原始图像")
self.checkBox2 = QtWidgets.QCheckBox("输出图像")
GroupBox2Layout = QtWidgets.QHBoxLayout()
GroupBox2Layout.addWidget(self.checkBox1)
GroupBox2Layout.addWidget(self.checkBox2)
self.GroupBox2.setLayout(GroupBox2Layout)
self.checkBox1.stateChanged.connect(self.box_choose)
self.checkBox2.stateChanged.connect(self.box_choose)
self.checkBox1.setChecked(True); self.checkBox2.setChecked(True)
self.GroupBox4 = QtWidgets.QGroupBox("文件设置")
self.GroupBox4.setFixedSize(850, 160)
self.filesButton = self.createButton("选择文件", self.filesButton_fuc)
self.outButton = self.createButton("输出目录", self.outButton_fuc)
self.bkfileButton = self.createButton("背景文件", self.bkfileButton_fuc)
self.filesButton.setToolTip("选择即将被替换背景的图片文件,可单选、多选...")
self.outButton.setToolTip("选择输出文件目录,替换后的文件将存在此目录...")
self.bkfileButton.setToolTip("选择可用作背景的图片文件,建议分辨率:1920x1080...")
self.filesButton.setFixedSize(80,23); self.outButton.setFixedSize(80,23)
self.bkfileButton.setFixedSize(80,23)
self.txt1 = QLabel('请选择图像文件[Ctrl+A全选、Ctrl/Shift+鼠标可多选]......', self); self.txt2 = QLabel('输出目录', self)
self.txt3 = QLabel('背景文件', self); self.txt4 = QLabel('纯色文件', self)
self.txt2.setText(out_dir); self.txt3.setText(bg_file);
self.txt4.setPixmap(self.CvMatToQImage(cv2.resize(bk_img, (50, 18))))
self.checkBox3_0 = QtWidgets.QCheckBox("只输出PNG文件(不换背景) ")
self.checkBox3_0.setToolTip("勾选:只输出透明的PNG文件,不更换背景;\n不勾选:同时输出PNG文件和更换背景的文件。");
self.checkBox3 = QtWidgets.QCheckBox("纯色背景")
self.checkBox3_0.stateChanged.connect(self.box_choose3_0)
self.checkBox3.stateChanged.connect(self.box_choose3)
self.checkBox3_0.setChecked(True)
self.red = QLabel(' 红', self); self.green= QLabel(' 绿', self); self.blue = QLabel(' 蓝', self)
self.red_e = QLineEdit(self); self.red_e.setText('8')
self.green_e = QLineEdit(self); self.green_e.setText('188')
self.blue_e = QLineEdit(self); self.blue_e.setText('8')
self.red_e.setValidator(QIntValidator(0, 254))
self.green_e.setValidator(QIntValidator(0, 254))
self.blue_e.setValidator(QIntValidator(0, 254))
self.red_e.setFixedSize(28, 20);self.green_e.setFixedSize(28, 20);self.blue_e.setFixedSize(28, 20)
self.red_e.textChanged[str].connect(self.red_e_fuc)
self.green_e.textChanged[str].connect(self.green_e_fuc)
self.blue_e.textChanged[str].connect(self.blue_e_fuc)
layout_box1 = QtWidgets.QHBoxLayout()
layout_box2 = QtWidgets.QHBoxLayout()
layout_box3 = QtWidgets.QHBoxLayout()
layout_box1.addWidget(self.filesButton, Qt.AlignLeft| Qt.AlignVCenter)
layout_box1.addWidget(self.txt1, Qt.AlignLeft| Qt.AlignVCenter)
layout_box2.addWidget(self.outButton)
layout_box2.addWidget(self.txt2)
layout_box2.addWidget(self.bkfileButton)
layout_box2.addWidget(self.txt3)
layout_box3.addWidget(self.checkBox3_0)
layout_box3.addWidget(self.checkBox3)
layout_box3.addWidget(self.txt4)
layout_box3.addWidget(self.red)
layout_box3.addWidget(self.red_e)
layout_box3.addWidget(self.green)
layout_box3.addWidget(self.green_e)
layout_box3.addWidget(self.blue)
layout_box3.addWidget(self.blue_e)
layout_box3.addStretch(1)
layout11 = QWidget(); layout21 = QWidget(); layout31 = QWidget()
layout11.setLayout(layout_box1); layout21.setLayout(layout_box2); layout31.setLayout(layout_box3)
GroupBoxmainLayout = QtWidgets.QVBoxLayout()
GroupBoxmainLayout.addWidget(layout11, Qt.AlignLeft | Qt.AlignVCenter)
GroupBoxmainLayout.addWidget(layout21, Qt.AlignLeft | Qt.AlignVCenter)
GroupBoxmainLayout.addWidget(layout31, Qt.AlignLeft | Qt.AlignVCenter)
self.GroupBox4.setLayout(GroupBoxmainLayout)
self.GroupBox5 = QtWidgets.QGroupBox("信息统计")
self.GroupBox5.setFixedSize(850, 90)
self.txt11 = QLabel('【图像信息】', self)
self.txt12 = QLabel('【运行信息】', self)
GroupBox5Layout = QtWidgets.QGridLayout()
GroupBox5Layout.addWidget(self.txt11, 0, 1)
GroupBox5Layout.addWidget(self.txt12, 1, 1)
self.GroupBox5.setLayout(GroupBox5Layout)
self.startButton = self.createButton("开始处理", self.startrun)
self.stopButton = self.createButton("停止", self.stoprun)
self.helpButton = self.createButton("帮助", self.helpWin)
self.quitButton = self.createButton("退出", self.quitWin)
self.startButton.setFixedSize(80,25)
self.stopButton.setFixedSize(55, 25)
self.helpButton.setFixedSize(55,25)
self.quitButton.setFixedSize(55,25)
topLayout2.addWidget(self.GroupBox4)
topLayout3.addWidget(self.GroupBox5)
topLayout4.addWidget(self.GroupBox1)
topLayout4.addWidget(self.GroupBox2)
topLayout4.addWidget(self.startButton)
topLayout4.addWidget(self.stopButton)
topLayout4.addWidget(self.helpButton)
topLayout4.addWidget(self.quitButton)
topLayout4.setSpacing(20)
layout1 = QWidget(); layout2 = QWidget()
layout3 = QWidget(); layout4 = QWidget()
layout1.setLayout(topLayout1); layout2.setLayout(topLayout2)
layout3.setLayout(topLayout3); layout4.setLayout(topLayout4)
mainLayout.addWidget(layout1, Qt.AlignLeft | Qt.AlignTop)
mainLayout.addWidget(layout2, Qt.AlignLeft | Qt.AlignBottom)
mainLayout.addWidget(layout3, Qt.AlignLeft | Qt.AlignBottom)
mainLayout.addWidget(layout4, Qt.AlignLeft | Qt.AlignBottom)
self.setLayout(mainLayout)
def createButton(self, text, member):
button = QtWidgets.QPushButton(text)
button.clicked.connect(member)
return button