2022年 11月 9日

python计算机视觉– 基于OpenCV的图像分割和图像融合系统

目录

前言

一、需求分析

二、概要设计

2.1 基本原理                                                                                                                                     

2.2 界面设计

三、详细设计

3.1 系统流程图

3.2 数据集

3.3 代码实现

3.3.1 利用deeplabV3模型分割

 3.3.2 利用grabCut分割

3.3.3 Alpha Blending图像融合

3.3.4 UI界面

四、实验结果与分析

总结


前言

记录记录计算机视觉期末大作业,利用OpenCV实现了人像的前后景分割,GrabCut可以实现所有类型的图像分割,DeeplavbV3模型调用飞桨的deeplabv3p_xception65_humanseg,该模型只能用于人像抠图。

环境:python3.8.5,pycharm,OpenCV库


一、需求分析

        现实生活中,我们经常会遇到将图像前景和背景分开的情况,比如证件照背景替换。在一幅图像中,人们可能只对其中的部分目标感兴趣,这些目标通常占据一定的区域,并且在某些特性上和临近的图像有差别。因此我们需要将图像前景与背景分割,以便用于产品检验,目标提取,图像融合等方面。所以系统的设计应具有如下方面:

(1)系统的输入;

        用户点击选择图片按钮,将一张需要分割的图片读入系统。

(2)系统的输出;

        通过分割算法将分割所得前景图像显示在界面上,用户可对结果图进行背景替换或保存。

(3)系统实现的功能。

        系统实现了利用百度开源库deeplabv3+和grabCut实现了图像前景和背景的分割,利用Alpha blending实现了背景替换,Poisson Blending实现两幅图像的融合,通过pyqt设计UI界面实现了图像的点击操作。

二、概要设计

2.1 基本原理                                                                                                                                     

(1)百度开源库deeplabV3+

        通过查阅资料新了解了deeplabV3+:DeeplabV3+语义分割领域模型效果非常好的方法。DeeplabV3+主要在原有模型的基础上引入了可任意控制编码器提取特征的分辨率,通过在Encoder部分加入大量空洞卷积平衡精度和耗时。在不损失信息的情况下,加大了感受野,让每个卷积输出都包含较大范围的信息。

空洞卷积:

                                                        图 2-1 空洞卷积示意图

加入了空洞卷积,调整filters的接受野,如上图所示,左图中标准卷积中的卷积核大小为 3×3,其感受野也为 3×3,在卷积核中间插入 0 之后变为右图空洞卷积,其中实际参与计算的卷积核大小仍为 3×3,而感受野已经扩大到了 5×5。感受野增大后可以检测分割大目标,另一方面分辨率高了可以精确定位目标。

DeeplabV3+的模型介绍:

                                                        图2-2 DeeplabV3模型

 输入一张图片,首先进入Encoder进行特征提取,利用深度卷积神经网络可以得到两个特征层,浅层特征送入Decoder,对深层特征利用不同膨胀率的膨胀卷积进行更深层次的特征提取,不同膨胀率的膨胀卷积提高了网络的感受野,之后对堆叠的特征进行过滤后送入Decoder,经过上采样和原来的浅层特征卷积结果融合,再次进行3×3特征特取,最后将输出图像调整到和输入图像一样,得到最终结果。

 (2)图像分割:grabCut算法

graph Cut算法示意图:

                                                         图2-3 graph Cut算法示意图

Grabcut是基于图割(graph cut)实现的图像分割算法,它需要用户输入一个矩形框作为分割目标位置,实现对目标与背景的分割,位于框内部的属于前景,框外面的属于背景。Graph cut 算法是根据该像素点灰度值,在前景和背景中的灰度值直方图所占的比例来计算前景和背景概率的,求某个像素点lp与相邻像素点lq之间的(灰度值)差异,lp-lq的值越大,两个像素之间的边越是应该是割边。在Grab cut 中,不是以灰度为基准,而是以BGR三通道衡量两像素的相似性,采用欧式距离计算两个像素之间的差异。在计算区域项时,采用了高斯混合模型,在多个模型计算式,选取概率最大的一个。区域项反映的是,像素样本集合的整体特性,边界项反映的是两个像素之间的差异。在经过处理得到的掩膜中有四个值,分别表示前景,背景,可能前景,可能背景,合并前景和可能前景得到分割结果。

2.2 界面设计

界面由三部分组成,左侧是功能按钮区,包括开始分割、保存、背景替换、拍摄图像按钮,中间是图像显示区,有original Image 和result Image;下面是图片选择按钮和运行时间显示区。下图所示:

                                                         图2-4 初始化界面

                                                        图2-5 grabCut分割示例

三、详细设计

3.1 系统流程图

        首先读取图像并进行USM锐化,增强图像细节,便于提取前景;手动选择目标前景区域ROI,进行grabCut图像分割,提取前景ROI区域的二值图像;将得到的前景ROI区域二值图像利用形态学滤波进行开运算消除细微干扰,接着与锐化图像进行与运算得到前景图像。

                                                     图2-6 grabCut图像分割示意图

Alpha Blending图像融合实现:读入分割得到的前景图像,分离出alpha掩膜,对掩膜腐蚀过滤掉其中白点,对腐蚀图像膨胀消除噪声,利用alpha掩膜对前景和背景进行加权,得到融合结果图像。

                                                    图2-7 Alpha Blending流程图

3.2 数据集

选取作为分割和融合测试的数据集如下,可任选电脑上图片进行测试:

3.3 代码实现

3.3.1 利用deeplabV3模型分割

将图像路径传入函数,调用飞桨训练模型实现分割,保存分割的前景图像,将轮廓图像进行阈值处理,图像腐蚀去除噪声,再进行图像膨胀处理恢复原来形状,删除累计分割结果,减少存储。

  1. # 导入库
  2. from PIL import Image
  3. import numpy as np
  4. import cv2
  5. import os
  6. # PaddleHub是基于PaddlePaddle生态下的预训练模型管理和迁移学习工具
  7. # 可以结合预训练模型更便捷地开展迁移学习工作,通过PaddleHub,可以便捷地获取PaddlePaddle生态下的所有预训练模型
  8. import paddlehub as hub
  9. import matplotlib.pyplot as plt
  10. import matplotlib.image as mping
  11. def img_remove(remove_img_path):
  12. # 删除原有路径中.jpg格式文件
  13. for root, dirs, files in os.walk(remove_img_path):
  14. for file in files:
  15. file_path = os.path.join(root, file)
  16. if str(file_path.split('.')[-1]).upper() == 'PNG':
  17. os.remove(file_path)
  18. print('删除完成')
  19. # 用matplotlib显示中文,避免乱码
  20. plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
  21. plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
  22. def paddle_cut_Image(imgPath):
  23. """利用deeplabv3p分割图像"""
  24. # 1.获取图片路径,传入模型
  25. test_img_path = imgPath
  26. # 2.调用飞桨训练模型
  27. # 调用飞桨的deeplabv3p_xception65_humanseg,该模型用于人像抠图
  28. moddle = hub.Module(name="deeplabv3p_xception65_humanseg")
  29. # 训练模型并预测模型,打印结果(获取到抠图人像)
  30. result = moddle.segmentation(images=[cv2.imread(test_img_path)], visualization=True,
  31. output_dir='../humanseg_output')
  32. # 3.保存抠出的前景图像
  33. paddle_fore_img = mping.imread(result[0]['save_path'])
  34. mping.imsave("paddle_output/paddle_output.png", paddle_fore_img)
  35. # 4.保存轮廓图像
  36. res_image = Image.fromarray(np.uint8(result[0]['data']))
  37. path = "paddle_mask/paddle_mask.png"
  38. res_image.save(path)
  39. # 加载保存的轮廓并显示,接着对其进行二值化处理,然后进行图像的腐蚀膨胀操作,得到输出结果,根据结果调整参数值。
  40. counter_img_path = path
  41. img_counter = cv2.imread(counter_img_path)
  42. # 灰度图像
  43. img_gray = cv2.cvtColor(img_counter, cv2.COLOR_BGR2GRAY)
  44. # 阈值处理,图像二值化(ret:阈值,thresh1:阈值化后的图像)
  45. ret, thresh1 = cv2.threshold(img_gray, 200, 255, cv2.THRESH_BINARY)
  46. # getStructuringElement( ) 返回指定形状和尺寸的结构元素
  47. kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
  48. # 图像腐蚀,去除了噪声,但是会压缩图像。
  49. erode = cv2.erode(thresh1, kernel, iterations=2)
  50. # 对腐蚀的图像膨胀处理,可以去除噪声,并且保持原有形状
  51. dilate = cv2.dilate(erode, kernel, iterations=2)
  52. cv2.imwrite(path, dilate)
  53. # cv2.imshow('dilate',dilate)
  54. # # cv2.imshow('erode',erode)
  55. # # cv2.imshow('gray_img',img_gray)
  56. # # cv2.imshow('binary_img',thresh1)
  57. # # cv2.imshow('counter_img',img_counter)
  58. # cv2.waitKey(0)
  59. # cv2.destroyAllWindows()
  60. # 5.清理文件夹残存图像
  61. remove_img_path = r'/humanseg_output'
  62. img_remove(remove_img_path)
  63. current_working_dir = os.getcwd()
  64. paddle_fore_img_path = os.path.join(current_working_dir, "paddle_output\paddle_output.png")
  65. return paddle_fore_img_path

 3.3.2 利用grabCut分割

对图像进行USM锐化增强:通过增加图像边缘的对比度来锐化并给图像添加细节。USM锐化增强方法(Unsharpen Mask):

1.先对原图高斯模糊,用原图减去系数x高斯模糊的图像

2.再把值Scale到0~255的RGB像素范围

3.公式:(原图像-w*高斯模糊)/(1-w);w表示权重(0.1~0.9),默认0.6

优点:可以去除一些细小细节的干扰和噪声,比卷积更真实。

读取图像并进行USM锐化,增强图像细节,便于提取前景;手动选择目标前景区域ROI,进行grabCut图像分割,提取前景ROI区域的二值图像;将得到的前景ROI区域二值图像利用形态学滤波进行开运算消除细微干扰,接着与锐化图像进行与运算得到前景图像。

  1. import os
  2. import cv2
  3. import numpy as np
  4. def img_usm(img):
  5. # 对图像进行USM锐化增强:通过增加图像边缘的对比度来锐化并给图像添加细节
  6. # sigma = 5、15、25
  7. # 模糊图像(img:原图像,高斯核(等于0则根据sigmaX和sigmaY计算得出),标准差)
  8. blur_img = cv2.GaussianBlur(img, (0, 0), 5)
  9. # cv.addWeighted(图1,权重1, 图2, 权重2, gamma修正系数, dst可选参数, dtype可选参数)
  10. usmimg = cv2.addWeighted(img, 1.5, blur_img, -0.5, 0)
  11. cv2.convertScaleAbs(usmimg,usmimg)
  12. return usmimg
  13. def img_grab_Cut(original_img,usmimg,rect):
  14. """进行grabCut图像分割"""
  15. # 掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;
  16. # 在执行分割的时候,也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果
  17. mask = np.zeros(usmimg.shape[:2], np.uint8) # 初始化蒙版图像
  18. bgdModel = np.zeros((1, 65), np.float64) # 背景模型
  19. fgdModel = np.zeros((1, 65), np.float64) # 前景模型
  20. cv2.grabCut(usmimg, mask, rect, bgdModel, fgdModel, 20, cv2.GC_INIT_WITH_RECT) # 函数返回值为mask,bgdModel,fgdModel
  21. # 提取前景ROI区域二值图像
  22. foreground = np.zeros(original_img.shape[:3],np.uint8)
  23. foreground_roi = np.zeros(original_img.shape[:3], np.uint8)
  24. height = original_img.shape[0]
  25. width = original_img.shape[1]
  26. for row in range(height):
  27. for col in range(width):
  28. if mask[row,col] == 1 or mask[row,col] == 3:
  29. foreground_roi[row,col] = [255,255,255]
  30. cv2.imwrite('grabcut_mask/mask.png', foreground_roi)
  31. # 将得到的前景ROI区域二值图像进行开运算消除细微干扰后,和锐化图像进行与( and )操作,得到锐化后的前景区域
  32. # cv2.getStructuringElement:返回指定形状和尺寸的结构元素
  33. kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))
  34. # cv2.morphologyEx:形态学滤波,cv2.MORPH_OPEN:开运算(open):先腐蚀后膨胀的过程。开运算可以用来消除小黑点
  35. cv2.morphologyEx(foreground_roi, cv2.MORPH_OPEN, kernel)
  36. cv2.bitwise_and(foreground_roi, usmimg, foreground)
  37. return foreground
  38. def main(imgPath):
  39. # 读取图像
  40. img = cv2.imread(imgPath)
  41. img2 = update_img_resize(img)
  42. # usm锐化
  43. usmimg = img_usm(img2)
  44. cv2.imshow("mask image", usmimg)
  45. rect = cv2.selectROI("mask image", usmimg, fromCenter=False, showCrosshair=False)
  46. print(rect)
  47. print(rect[0],rect[1],rect[2],rect[3])
  48. # 进行grabCut图像分割
  49. grabcut_img= img_grab_Cut(img2,usmimg, rect)
  50. # print(grabcut_img.shape,grabcut_img.dtype)
  51. print("分割完成")
  52. cv2.imwrite('grabcut_output/test.png', grabcut_img) # 写入图片
  53. current_working_dir = os.getcwd()
  54. grab_fore_img_path = os.path.join(current_working_dir, "grabcut_output\\test.png")
  55. return grab_fore_img_path
  56. def update_img_resize(img):
  57. """修改图像尺寸"""
  58. # result_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
  59. result_img = cv2.resize(img,dsize=(480,640))
  60. return result_img
  61. if __name__ == '__main__':
  62. main()

3.3.3 Alpha Blending图像融合

读入分割得到的前景图像,分离出alpha掩膜,对掩膜腐蚀过滤掉其中白点,腐蚀消除噪声的同时会压缩图像,接着对腐蚀图像膨胀,恢复原来形状,将alpha的值归一化在0~1之间作为加权系数,利用alpha掩膜对前景和背景进行加权,得到融合结果图像。

  1. from Img_Segmentation import grabCut3
  2. import cv2
  3. def alpha_blending(fore_img_path,bg_img_path):
  4. """Alpha_Blending"""
  5. # foreGroundImage = cv2.imread('../paddle_output/paddle_output.png', -1)
  6. foreGroundImage = cv2.imread(fore_img_path, -1)
  7. foreGroundImage = grabCut3.update_img_resize(foreGroundImage)
  8. # 先将通道分离
  9. b, g, r, a = cv2.split(foreGroundImage)
  10. # 得到PNG图像前景部分,在这个图片中就是除去Alpha通道的部分
  11. foreground = cv2.merge((b, g, r))
  12. # 得到PNG图像的alpha通道,即alpha掩模
  13. alpha = cv2.merge((a, a, a))
  14. # 腐蚀膨胀过滤掉图中的白点
  15. erode_alpha = cv2.erode(alpha, None, iterations=1) # 图像被腐蚀后,去除了噪声,但是会压缩图像。
  16. alpha = cv2.dilate(erode_alpha, None, iterations=1) # 对腐蚀过的图像,进行膨胀处理,可以去除噪声,并且保持原有形状
  17. # background = cv2.imread(r'D:\python\RRJ\pycharmproject\Practice\chep2\bdd\green.png')
  18. background = cv2.imread(bg_img_path)
  19. background = grabCut3.update_img_resize(background)
  20. # 因为下面要进行乘法运算故将数据类型设为float,防止溢出
  21. foreground = foreground.astype(float)
  22. background = background.astype(float)
  23. cv2.imwrite("alpha/alpha.png", alpha)
  24. # 将alpha的值归一化在0-1之间,作为加权系数
  25. alpha = alpha.astype(float) / 255
  26. # cv2.imshow("alpha", alpha)
  27. # cv2.waitKey(0)
  28. # 将前景和背景进行加权,每个像素的加权系数即为alpha掩模对应位置像素的值,前景部分为1,背景部分为0
  29. foreground = cv2.multiply(alpha, foreground)
  30. background = cv2.multiply(1.0 - alpha, background)
  31. outImage = cv2.add(foreground, background)
  32. save_path = fore_img_path
  33. cv2.imwrite(save_path, outImage)
  34. # cv2.imshow("outImg", outImage / 255)
  35. # cv2.waitKey(0)
  36. return save_path

3.3.4 UI界面

  1. import os
  2. import time
  3. import cv2
  4. from PyQt5 import QtCore, QtGui, QtWidgets
  5. from PyQt5.QtGui import QPixmap
  6. from PyQt5.QtWidgets import QFileDialog
  7. from PyQt5.QtCore import Qt
  8. from Img_Segmentation import grabCut3, paddle_Image, poisson_blending, takephotos
  9. from Img_Segmentation import Alpha_Blending
  10. global imgNamepath
  11. global flag
  12. class Ui_MainWindow(object):
  13. def setupUi(self, MainWindow):
  14. MainWindow.setObjectName("图像分割")
  15. # # 给MainWindow设置图标
  16. # MainWindow.setWindowIcon(QIcon(r'D:\\download\\xj.ico')) # 路径错误找不到问题所在
  17. #
  18. # # 给MainWindow设置背景图片
  19. # palette = QPalette()
  20. # palette.setBrush(QPalette.Background, QBrush(QPixmap('D:\\python\\RRJ\\pycharmproject\\Practice\\chep2\\bdd'
  21. # '\\blue_bg.jpg')))
  22. # MainWindow.setPalette(palette)
  23. MainWindow.resize(1232, 852)
  24. self.centralwidget = QtWidgets.QWidget(MainWindow)
  25. self.centralwidget.setObjectName("centralwidget")
  26. self.widget = QtWidgets.QWidget(self.centralwidget)
  27. self.widget.setGeometry(QtCore.QRect(10, 0, 1211, 791))
  28. self.widget.setStyleSheet("border:1px solid black;\n")
  29. self.widget.setObjectName("widget")
  30. self.label = QtWidgets.QLabel(self.widget)
  31. self.label.setGeometry(QtCore.QRect(0, 0, 1211, 101))
  32. self.label.setStyleSheet("font: 24pt \"Arial\";\n"
  33. "color:rgb(255, 170, 0);\n"
  34. "text-aligen:center;")
  35. self.label.setObjectName("label")
  36. self.label_2 = QtWidgets.QLabel(self.widget)
  37. self.label_2.setGeometry(QtCore.QRect(260, 180, 431, 451))
  38. self.label_2.setStyleSheet("background-color:rgb(255, 255, 255);")
  39. self.label_2.setText("原始图像显示区")
  40. self.label_2.setAlignment(Qt.AlignCenter)
  41. self.label_2.setStyleSheet("font: 14pt \"Arial\";\n")
  42. self.label_2.setObjectName("label_2")
  43. self.label_3 = QtWidgets.QLabel(self.widget)
  44. self.label_3.setGeometry(QtCore.QRect(750, 180, 431, 451))
  45. self.label_3.setStyleSheet("background-color:rgb(255, 255, 255);")
  46. self.label_3.setText("结果显示区")
  47. self.label_3.setAlignment(Qt.AlignCenter)
  48. self.label_3.setStyleSheet("font: 14pt \"Arial\";\n")
  49. self.label_3.setObjectName("label_3")
  50. self.label_4 = QtWidgets.QLabel(self.widget)
  51. self.label_4.setGeometry(QtCore.QRect(370, 130, 181, 41))
  52. self.label_4.setStyleSheet("border:0px;\n"
  53. "font: 14pt \"Arial\";\n")
  54. self.label_4.setObjectName("label_4")
  55. self.label_5 = QtWidgets.QLabel(self.widget)
  56. self.label_5.setGeometry(QtCore.QRect(890, 130, 131, 41))
  57. self.label_5.setStyleSheet("font: 14pt \"Arial\";\n"
  58. "border:0px;")
  59. self.label_5.setObjectName("label_5")
  60. self.pushButton = QtWidgets.QPushButton(self.widget)
  61. self.pushButton.setGeometry(QtCore.QRect(261, 660, 150, 51))
  62. self.pushButton.setStyleSheet("font: 12pt \"Arial\";")
  63. self.pushButton.setObjectName("pushButton")
  64. self.lineEdit = QtWidgets.QLineEdit(self.widget)
  65. self.lineEdit.setGeometry(QtCore.QRect(410, 660, 771, 51))
  66. self.lineEdit.setStyleSheet("font: 12pt \"Arial\";\n")
  67. self.lineEdit.setObjectName("lineEdit")
  68. self.pushButton_2 = QtWidgets.QPushButton(self.widget)
  69. self.pushButton_2.setGeometry(QtCore.QRect(261, 720, 150, 51))
  70. self.pushButton_2.setStyleSheet("font: 12pt \"Arial\";")
  71. self.pushButton_2.setObjectName("pushButton_2")
  72. self.lineEdit_2 = QtWidgets.QLineEdit(self.widget)
  73. self.lineEdit_2.setGeometry(QtCore.QRect(410, 720, 771, 51))
  74. self.lineEdit_2.setStyleSheet("font: 12pt \"Arial\";\n")
  75. self.lineEdit_2.setObjectName("lineEdit_2")
  76. self.label_6 = QtWidgets.QLabel(self.widget)
  77. self.label_6.setGeometry(QtCore.QRect(0, 100, 221, 691))
  78. self.label_6.setText("")
  79. self.label_6.setObjectName("label_6")
  80. self.pushButton_3 = QtWidgets.QPushButton(self.widget)
  81. self.pushButton_3.setGeometry(QtCore.QRect(30, 200, 151, 51))
  82. self.pushButton_3.setStyleSheet("font: 14pt \"Arial\";")
  83. self.pushButton_3.setObjectName("pushButton_3")
  84. self.pushButton_4 = QtWidgets.QPushButton(self.widget)
  85. self.pushButton_4.setGeometry(QtCore.QRect(30, 290, 151, 51))
  86. self.pushButton_4.setStyleSheet("font: 14pt \"Arial\";\n")
  87. self.pushButton_4.setObjectName("pushButton_4")
  88. self.pushButton_5 = QtWidgets.QPushButton(self.widget)
  89. self.pushButton_5.setGeometry(QtCore.QRect(30, 460, 151, 51))
  90. self.pushButton_5.setStyleSheet("font: 14pt \"Arial\";")
  91. self.pushButton_5.setObjectName("pushButton_5")
  92. self.pushButton_6 = QtWidgets.QPushButton(self.widget)
  93. self.pushButton_6.setGeometry(QtCore.QRect(30, 550, 151, 51))
  94. self.pushButton_6.setStyleSheet("font: 14pt \"Arial\";")
  95. self.pushButton_6.setObjectName("pushButton_6")
  96. self.pushButton_7 = QtWidgets.QPushButton(self.widget)
  97. self.pushButton_7.setGeometry(QtCore.QRect(30, 370, 151, 51))
  98. self.pushButton_7.setStyleSheet("font: 14pt \"Arial\";")
  99. self.pushButton_7.setObjectName("pushButton_7")
  100. self.pushButton_8 = QtWidgets.QPushButton(self.widget)
  101. self.pushButton_8.setGeometry(QtCore.QRect(30, 640, 151, 51))
  102. self.pushButton_8.setStyleSheet("font: 14pt \"Arial\";")
  103. self.pushButton_8.setObjectName("pushButton_8")
  104. MainWindow.setCentralWidget(self.centralwidget)
  105. self.menubar = QtWidgets.QMenuBar(MainWindow)
  106. self.menubar.setGeometry(QtCore.QRect(0, 0, 1232, 26))
  107. self.menubar.setObjectName("menubar")
  108. MainWindow.setMenuBar(self.menubar)
  109. self.statusbar = QtWidgets.QStatusBar(MainWindow)
  110. self.statusbar.setObjectName("statusbar")
  111. MainWindow.setStatusBar(self.statusbar)
  112. self.retranslateUi(MainWindow)
  113. QtCore.QMetaObject.connectSlotsByName(MainWindow)
  114. # 按钮关联函数
  115. self.pushButton.clicked.connect(self.openImage)
  116. self.pushButton_3.clicked.connect(self.start_paddle_Image)
  117. self.pushButton_4.clicked.connect(self.start_grabcut)
  118. self.pushButton_5.clicked.connect(self.saveImage)
  119. self.pushButton_6.clicked.connect(self.img_poisson_blend)
  120. self.pushButton_7.clicked.connect(self.take_pictures)
  121. self.pushButton_8.clicked.connect(self.alpha_blending_img)
  122. def retranslateUi(self, MainWindow):
  123. _translate = QtCore.QCoreApplication.translate
  124. MainWindow.setWindowTitle(_translate("MainWindow", "图像分割"))
  125. self.label.setText(_translate("MainWindow", " 图像分割及图像融合系统"))
  126. self.label_4.setText(_translate("MainWindow", " original Image"))
  127. self.label_5.setText(_translate("MainWindow", "result Image"))
  128. self.pushButton.setText(_translate("MainWindow", "选择图片"))
  129. self.pushButton_2.setText(_translate("MainWindow", "运行时间"))
  130. self.pushButton_3.setText(_translate("MainWindow", "开始分割"))
  131. self.pushButton_4.setText(_translate("MainWindow", "grabCut"))
  132. self.pushButton_5.setText(_translate("MainWindow", "保 存"))
  133. self.pushButton_6.setText(_translate("MainWindow", "图像融合"))
  134. self.pushButton_7.setText(_translate("MainWindow", "拍摄图像"))
  135. self.pushButton_8.setText(_translate("MainWindow", "背景替换"))
  136. # 选择本地图片上传
  137. def openImage(self):
  138. global imgNamepath # 这里为了方便别的地方引用图片路径,将其设置为全局变量
  139. # 弹出一个文件选择框,第一个返回值imgName记录选中的文件路径+文件名,第二个返回值imgType记录文件的类型
  140. # QFileDialog就是系统对话框的那个类第一个参数是上下文,第二个参数是弹框的名字,第三个参数是默认打开的路径,第四个参数是需要的格式
  141. imgNamepath, imgType = QFileDialog.getOpenFileName(self.centralwidget, "选择图片",
  142. "D:\\python\\RRJ\\pycharmproject\\Practice\\chep2\\bdd2",
  143. "*.jpg;;*.png;;All Files(*)")
  144. # 通过文件路径获取图片文件,并设置图片长宽为label控件的长、宽
  145. # img = QtGui.QPixmap(imgNamepath).scaled(self.label_2.width(), self.label_2.height())
  146. imgShow = QtGui.QPixmap(imgNamepath)
  147. self.label_2.setScaledContents(True)
  148. self.label_2.setPixmap(imgShow)
  149. # 显示所选图片的路径
  150. self.lineEdit.setText(imgNamepath)
  151. # 执行分割
  152. def start_paddle_Image(self):
  153. tstart = time.time()
  154. global flag
  155. flag = 1
  156. pdcut_img_path = paddle_Image.paddle_cut_Image(imgNamepath)
  157. pdcut_img = QPixmap(pdcut_img_path)
  158. self.label_3.setScaledContents(True)
  159. self.label_3.setPixmap(pdcut_img)
  160. tend = time.time()
  161. result = tend - tstart
  162. self.lineEdit_2.setText(str('%.3f' % float(result)) + 's')
  163. # grabCut_img
  164. def start_grabcut(self):
  165. tstart = time.time()
  166. global flag
  167. flag = 2
  168. gb_img_path = grabCut3.main(imgNamepath)
  169. # print(pdcut_img_path)
  170. gb_img = QPixmap(gb_img_path)
  171. self.label_3.setScaledContents(True)
  172. self.label_3.setPixmap(gb_img)
  173. tend = time.time()
  174. result = tend - tstart
  175. self.lineEdit_2.setText(str('%.3f' % float(result)) + 's')
  176. # 保存图片到本地(第二种方式:首先提取相对应Qlabel中的图片,然后打开一个保存文件的弹出框,最后保存图片到选中的路径)
  177. def saveImage(self):
  178. # 提取Qlabel中的图片
  179. img = self.label_3.pixmap().toImage()
  180. # print(type(img))
  181. fpath, ftype = QFileDialog.getSaveFileName(self.centralwidget, "保存图片", "d:\\", "*.png;;*.jpg;;All Files(*)")
  182. img.save(fpath)
  183. # 图像泊松融合
  184. def img_poisson_blend(self):
  185. tstart = time.time()
  186. self.openImage()
  187. if self.label_3.text() == "结果显示区":
  188. src_img = cv2.imread(imgNamepath)
  189. else:
  190. # 提取Qlabel中的图片
  191. src_img = self.label_3.pixmap().toImage()
  192. # 判断路径是否存在,如果不存在则新建
  193. path = "D:\\python\\RRJ\\pycharmproject\\Image_Segmentation\\PyQtImgZCQ\\"
  194. if not os.path.exists(path):
  195. os.mkdir(path)
  196. # 因为不知道怎么将<class 'numpy.ndarray'>转换为<class 'PyQt5.QtGui.QPixmap'>类型,因此采用暂存再读出的方式
  197. path = os.path.join(path, 'ZC.png')
  198. src_img.save(path)
  199. src_img = cv2.imread(path)
  200. # src_img = cv2.cvtColor(src_img,cv2.COLOR_RGB2BGR)
  201. poisson_img_path = poisson_blending.img_Poisson_Blending(src_img, imgNamepath)
  202. # pyqt5从路径读取图片
  203. imgShow = QPixmap(poisson_img_path)
  204. self.label_3.setScaledContents(True)
  205. self.label_3.setPixmap(imgShow)
  206. tend = time.time()
  207. result = tend - tstart
  208. self.lineEdit_2.setText(str('%.3f' % float(result)) + 's')
  209. # 拍摄照片
  210. def take_pictures(self):
  211. global imgNamepath
  212. imgNamepath = takephotos.take_photos()
  213. print(imgNamepath)
  214. show_take_img = QPixmap(imgNamepath)
  215. self.label_2.setScaledContents(True)
  216. self.label_2.setPixmap(show_take_img)
  217. # 背景替换
  218. def alpha_blending_img(self):
  219. tstart = time.time()
  220. self.openImage()
  221. fore_imgpath = ''
  222. if self.label_3.text() == "结果显示区":
  223. src_img = cv2.imread(imgNamepath)
  224. else:
  225. # 提取Qlabel中的图片
  226. src_img = self.label_3.pixmap().toImage()
  227. # 判断路径是否存在,如果不存在则新建
  228. path = "D:\\python\\RRJ\\pycharmproject\\Image_Segmentation\\PyQtImgZCQ\\"
  229. if not os.path.exists(path):
  230. os.mkdir(path)
  231. # 因为不知道怎么将<class 'numpy.ndarray'>转换为<class 'PyQt5.QtGui.QPixmap'>类型,因此采用暂存再读出的方式
  232. path = os.path.join(path, 'ZC.png')
  233. src_img.save(path)
  234. fore_imgpath = path
  235. # src_img = cv2.imread(path)
  236. Alpha_img_path = ''
  237. if flag == 1:
  238. Alpha_img_path = Alpha_Blending.alpha_blending(fore_imgpath, imgNamepath)
  239. if flag == 2:
  240. Alpha_img_path = Alpha_Blending.alpha_blending2(fore_imgpath, imgNamepath)
  241. # pyqt5从路径读取图片
  242. imgShow = QPixmap(Alpha_img_path)
  243. self.label_3.setScaledContents(True)
  244. self.label_3.setPixmap(imgShow)
  245. tend = time.time()
  246. result = tend - tstart
  247. self.lineEdit_2.setText(str('%.3f' % float(result)) + 's')

四、实验结果与分析

(1) 图像分割

                                                   图2-8 deeplabV3模型分割结果(1) 

                                               图2-9 grabCut分割结果(1)

 

                                          图2-10 deeplabV3模型分割结果(2) 

                                             图2-11 grabCut分割结果(2)

                                                 图2-12 deeplabV3模型分割结果(3) 

                                                  图2-13 grabCut分割结果(3)

 通过对比可以看到,deeplabV3模型的分割结果十分优秀,不论是单人还是多人图像,都可以得到很好的效果,几乎没有锯齿痕迹。grabCut分割结果在简单证件照的时候效果和deeplabV3模型效果近似相同,但是在具有复杂背景和多人的情况下分割锯齿痕迹明显,且有分割前景缺失现象,而且在某些情形下前景区域不好手动标定。在grabCut设置迭代次数20的时候二者在运行时间上都较慢,时间复杂度高。

(2)图像融合

                                                     图2-14 Poisson Blending结果 

利用OpenCV中的seamlessClone()函数实现,参数选择MIXED_CLONE,保留背景图像的细节,目标区域的梯度由原图像和背景图像组合计算得到,但是从结果来看是将两幅图像像素梯度计算进行了深度融合,不是单纯的背景替换,可以用于人物模糊等应用场景。

(3)背景替换(Alpha Blending

                                            图2-15 deeplabV3模型分割结果替换 

                                             图2-16  grabCut分割结果替换

总结

此次实验我实现利用opencv和百度开源库实现了图像分割与图像融合,对图割法有了更多了解,也体会了图割法在实际问题中的使用,grabCut是基本图割法的基础上增加迭代次数和高斯混合模型计算区域项,理论上迭代次数越多分割效果越好,是以牺牲时间复杂度来换取正确率,但是正式操作的时候需要人为标定前景区域,有容易出现误操作和前景难以准备标出的现象,导致分割结果不好。同时发现发现了语义分割领域一个十分优秀的模型deeplabV3,了解了它的基本理解,调用模型测试了分割结果确实相当优秀,主要问题是在cpu情况下运行相对较慢。在这次课设中我也学到了pyqt制作图形化界面的相关操作,也了解了更多Opencv中图像操作函数,但还存在一些不足,主要在于没有自己实现算法流程,缺乏对算法的深入理解,ui界面设置的不够流畅,无法避免实际应用中的误操作。

详细代码:github