我做的界面大概思路:点击 上传图片,显示待检验的照片
点击 U-net模型检测 会使用已经训练好的U-net模型进行预测
点击 OpenCV统计 会将预测结果进行轮廓检测,并统计所有轮廓的面积
在 处理结果 这个groupBox中进行显示
大致界面如下:

详细界面布局如下:

将已经设置好的UI界面保存为yy.ui
将该文件放到自己的项目中,例如我放到的位置如下:

创建一个测试文件my_UI.py,在同级目录下
其中:
self.ui = QUiLoader().load('yy.ui'),指定Qt页面的路径,因为我这个是同一级目录故之间导入即可yy.ui
这行代码的意思为:将yy.ui这个UI创建一个名为ui的对象
self.output_size = 480,规定图像的大小
其他的为加载U-net网络模型
def __init__(self):
# 从文件中加载UI定义
# 从 UI 定义中动态 创建一个相应的窗口对象
# 注意:里面的控件对象也成为窗口对象的属性了
# 比如 self.ui.button , self.ui.textEdit
self.ui = QUiLoader().load('yy.ui') # 这里的参数为ui的路径,对这个ui文件创建对象ui
"""
对某个控件进行操作会产生一个signal,常通过slot来进行处理signal
slot就是通过clicked.connect来绑定某个函数,这个函数用于处理signal
"""
self.output_size = 480
# 加载网络,图片单通道,分类为1。
net = UNet(n_channels=1, n_classes=1)
# 将网络拷贝到deivce中
net.to(device=device)
# 加载模型参数
net.load_state_dict(
torch.load('G:/PyCharm/workspace/my_test/yy_unet/best_model1.pth', map_location=device)) # todo 模型位置
# 测试模式
net.eval()
self.model = net
为每一个button绑定对应的处理函数,其中pushButton、pushButton_2、pushButton_3是yy.ui页面中的button对象,ui中必须有这些名称的button才行
pushButton:打开文件,获取预测图片路径
pushButton_2:通过U-net网络模型预测
pushButton_3:通过OpenCV获取轮廓个数以及面积(像素)
def window_init(self):
self.ui.pushButton.clicked.connect(self.file)
self.ui.pushButton_2.clicked.connect(self.unet)
self.ui.pushButton_3.clicked.connect(self.opencv)
点击pushButton即可选择图片,通过file函数进行处理
image_file 存放照片的路径
后面为例存放处理的图片,通过os.path.basename(image_file)单独抽取了图片的名称,方面后续存储照片命名
self.ui.label_5.setPixmap(image_file),通过label_5进行展示照片,也就是选择照片,然后展示一下
# 打开图片
def file(self):
print("已点击file")
FileDialog = QFileDialog(self.ui.pushButton)
# 设置可以打开任何文件
FileDialog.setFileMode(QFileDialog.AnyFile)
# 文件过滤
Filter = "(*.jpg,*.png,*.jpeg,*.bmp,*.gif)|*.jgp;*.png;*.jpeg;*.bmp;*.gif|All files(*.*)|*.*"
image_file, _ = FileDialog.getOpenFileName(self.ui.pushButton, 'open file', './',
'Image files (*.jpg *.gif *.png *.jpeg)') # 选择目录,返回选中的路径 'Image files (*.jpg *.gif *.png *.jpeg)'
# 判断是否正确打开文件
if not image_file:
QMessageBox.warning(self.ui.pushButton, "警告", "文件错误或打开文件失败!", QMessageBox.Yes)
return
print("读入文件成功")
print(image_file)
self.image_file = image_file
self.base_image = os.path.basename(image_file)
print(self.image_file)
print(self.base_image)
# 设置标签的图片
self.ui.label_5.setPixmap(image_file)
self.ui.label_5.setScaledContents(True) # 让图片自适应 label 大小
点击pushButton_2即可运行到unet函数进行处理
model = self.model,加载模型
# 开始检测
def unet(self):
print("U-net...")
model = self.model # 加载模型
output_size = self.output_size # 规定输入图片的大小
img = cv2.imread(self.image_file)# 读取file函数所打开的图片
origin_shape = img.shape
# print(origin_shape)
# 转为灰度图
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
img = cv2.resize(img, (512, 512))
# 转为batch为1,通道为1,大小为512*512的数组
img = img.reshape(1, 1, img.shape[0], img.shape[1])
# 转为tensor
img_tensor = torch.from_numpy(img)
# 将tensor拷贝到device中,只用cpu就是拷贝到cpu中,用cuda就是拷贝到cuda中。
img_tensor = img_tensor.to(device=device, dtype=torch.float32)
# 预测
pred = self.model(img_tensor)
# 提取结果
pred = np.array(pred.data.cpu()[0])[0]
# 处理结果
pred[pred >= 0.5] = 255
pred[pred < 0.5] = 0
# 保存图片
cv2.imwrite("E:/change_picture/{}".format(self.base_image), pred)# 将预测的结果进行保存
# 将预测的图片通过label_6进行展示
self.ui.label_6.setPixmap("E:/change_picture/{}".format(self.base_image))
self.ui.label_6.setScaledContents(True)
该函数的作用是在unet预测的结果之上,将已经识别出来的起毛起球绘制轮廓并且统计个数以及每个起毛起球的面积(像素)
def opencv(self):
print("start")
print(self.base_image)
print("E:/change_picture/{}".format(self.base_image))
img = cv2.imread("E:/change_picture/{}".format(self.base_image))
#cv2.imshow('img', img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为灰度图
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化 # 对图像进行二值处理,小于127为0,大于127为255
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
draw_img = img.copy() # 注意一定要copy要不然会对原图进行改变!!!
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2) # -1表示显示所有轮廓,(0,0,225)BGR表示红色,2为轮廓粗细
#cv2.imshow('-1 is All', res)
cv2.imwrite("E:/change_picture/result/{}".format(self.base_image), res) # 保存照片
# cv2.waitKey(0)
# cv2.destroyAllWindows()
self.ui.label_7.setPixmap("E:/change_picture/result/{}".format(self.base_image))
self.ui.label_7.setScaledContents(True)
self.ui.plainTextEdit.clear()
n = len(contours) # 轮廓数量
print(n)
coutoursImg = [] # 创建绘制轮廓列表
sum_all = 0
for i in range(n):
temp = np.zeros(draw_img.shape, np.uint8) # 把图像转换为矩阵
coutoursImg.append(temp) # 向列表追加矩阵信息
# 绘制轮廓:drawContours()
"""绘制轮廓:drawContours()
1、img2:等待绘制轮廓的图像
2、coutours:要绘制的轮廓(与findContours()函数输出的coutours参数一致《list类型即:列表类型》)
3、contourIdx:要绘制一条轮廓还是全部轮廓:如果参数是一个整数或者0表示绘制对应索引号的轮廓[如果是-1表示绘制全部轮廓]
4、color:绘制颜色,用B,G,R格式表示
5、thickness:表示绘制时的画笔粗细,如果为-1表示用实心绘制
6、lineType:表示绘制线轮廓时的线形
7、hierarchy:所输出的层次信息
8、maxLevel:控制轮廓层次的深度(如果是0表示绘制地0层轮廓)
9、offset:偏移参数使轮廓偏移一定位置用[ x , y ]坐标表示
"""
if cv2.contourArea(contours[i]) >= 0:#对轮廓面积进行筛选,要是太小的话就可以忽略不计
sum_all = sum_all + cv2.contourArea(contours[i])
print("第" + str(i + 1) + "个毛球的面积是:" + str(cv2.contourArea(contours[i])) + "个像素点")
self.ui.plainTextEdit.appendPlainText("第" + str(i + 1) + "个毛球的面积是:" + str(cv2.contourArea(contours[i])) + "个像素点")
#cv2.imshow("chulihou", draw_img)
# 键盘输入检测,参数为显示时长,如果参数为0就是键盘按下退出显示
cv2.waitKey()
# 关闭所有窗口
cv2.destroyAllWindows()
self.ui.label_8.setText(str(len(contours)))
self.ui.label_9.setText(str(sum_all))
self.ui.label_10.setText("1级")
调用上面所定义的beyondyanyu类即可
if __name__=="__main__":
app = QApplication([])
gui = beyondyanyu() #初始化
gui.window_init()
gui.ui.show() #将窗口控件显示在屏幕上
app.exit(app.exec_())# 进行死循环展示
import shutil
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import torch
from model.unet_model import UNet
import numpy as np
from PySide2.QtWidgets import QApplication, QMessageBox,QFileDialog,QMainWindow
from PySide2.QtUiTools import QUiLoader
import cv2
import os
# 窗口主类
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
class beyondyanyu():
def __init__(self):
# 从文件中加载UI定义
# 从 UI 定义中动态 创建一个相应的窗口对象
# 注意:里面的控件对象也成为窗口对象的属性了
# 比如 self.ui.button , self.ui.textEdit
self.ui = QUiLoader().load('yy.ui') # 这里的参数为ui的路径,对这个ui文件创建对象ui
"""
对某个控件进行操作会产生一个signal,常通过slot来进行处理signal
slot就是通过clicked.connect来绑定某个函数,这个函数用于处理signal
"""
self.output_size = 480
# 加载网络,图片单通道,分类为1。
net = UNet(n_channels=1, n_classes=1)
# 将网络拷贝到deivce中
net.to(device=device)
# 加载模型参数
net.load_state_dict(
torch.load('G:/PyCharm/workspace/my_test/yy_unet/best_model1.pth', map_location=device)) # todo 模型位置
# 测试模式
net.eval()
self.model = net
def window_init(self):
self.ui.pushButton.clicked.connect(self.file)
self.ui.pushButton_2.clicked.connect(self.unet)
self.ui.pushButton_3.clicked.connect(self.opencv)
# 打开图片
def file(self):
print("已点击file")
FileDialog = QFileDialog(self.ui.pushButton)
# 设置可以打开任何文件
FileDialog.setFileMode(QFileDialog.AnyFile)
# 文件过滤
Filter = "(*.jpg,*.png,*.jpeg,*.bmp,*.gif)|*.jgp;*.png;*.jpeg;*.bmp;*.gif|All files(*.*)|*.*"
image_file, _ = FileDialog.getOpenFileName(self.ui.pushButton, 'open file', './',
'Image files (*.jpg *.gif *.png *.jpeg)') # 选择目录,返回选中的路径 'Image files (*.jpg *.gif *.png *.jpeg)'
# 判断是否正确打开文件
if not image_file:
QMessageBox.warning(self.ui.pushButton, "警告", "文件错误或打开文件失败!", QMessageBox.Yes)
return
print("读入文件成功")
print(image_file) # 'C:\\', 默认C盘打开
self.image_file = image_file
self.base_image = os.path.basename(image_file)
print(self.image_file)
print(self.base_image)
# 设置标签的图片
self.ui.label_5.setPixmap(image_file) #输入为图片路径,比如当前文件内的logo.png图片
# self.label.setFixedSize(600, 400) # 设置显示固定尺寸,可以根据图片的像素长宽来设置
self.ui.label_5.setScaledContents(True) # 让图片自适应 label 大小
# 开始检测
def unet(self):
print("U-net...")
model = self.model
output_size = self.output_size
img = cv2.imread(self.image_file)
origin_shape = img.shape
# print(origin_shape)
# 转为灰度图
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
img = cv2.resize(img, (512, 512))
# 转为batch为1,通道为1,大小为512*512的数组
img = img.reshape(1, 1, img.shape[0], img.shape[1])
# 转为tensor
img_tensor = torch.from_numpy(img)
# 将tensor拷贝到device中,只用cpu就是拷贝到cpu中,用cuda就是拷贝到cuda中。
img_tensor = img_tensor.to(device=device, dtype=torch.float32)
# 预测
pred = self.model(img_tensor)
# 提取结果
pred = np.array(pred.data.cpu()[0])[0]
# 处理结果
pred[pred >= 0.5] = 255
pred[pred < 0.5] = 0
# 保存图片
#im0 = cv2.resize(pred, self.origin_shape)
cv2.imwrite("E:/change_picture/{}".format(self.base_image), pred)
# 目前的情况来看,应该只是ubuntu下会出问题,但是在windows下是完整的,所以继续
#self.right_img.setPixmap(QPixmap("images/tmp/single_result.jpg"))
self.ui.label_6.setPixmap("E:/change_picture/{}".format(self.base_image))
self.ui.label_6.setScaledContents(True)
def opencv(self):
print("start")
print(self.base_image)
print("E:/change_picture/{}".format(self.base_image))
img = cv2.imread("E:/change_picture/{}".format(self.base_image))
#cv2.imshow('img', img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为灰度图
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化 # 对图像进行二值处理,小于127为0,大于127为255
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
draw_img = img.copy() # 注意一定要copy要不然会对原图进行改变!!!
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2) # -1表示显示所有轮廓,(0,0,225)BGR表示红色,2为轮廓粗细
#cv2.imshow('-1 is All', res)
cv2.imwrite("E:/change_picture/result/{}".format(self.base_image), res) # 保存照片
# cv2.waitKey(0)
# cv2.destroyAllWindows()
self.ui.label_7.setPixmap("E:/change_picture/result/{}".format(self.base_image))
self.ui.label_7.setScaledContents(True)
self.ui.plainTextEdit.clear()
n = len(contours) # 轮廓数量
print(n)
coutoursImg = [] # 创建绘制轮廓列表
sum_all = 0
for i in range(n):
temp = np.zeros(draw_img.shape, np.uint8) # 把图像转换为矩阵
coutoursImg.append(temp) # 向列表追加矩阵信息
# 绘制轮廓:drawContours()
"""绘制轮廓:drawContours()
1、img2:等待绘制轮廓的图像
2、coutours:要绘制的轮廓(与findContours()函数输出的coutours参数一致《list类型即:列表类型》)
3、contourIdx:要绘制一条轮廓还是全部轮廓:如果参数是一个整数或者0表示绘制对应索引号的轮廓[如果是-1表示绘制全部轮廓]
4、color:绘制颜色,用B,G,R格式表示
5、thickness:表示绘制时的画笔粗细,如果为-1表示用实心绘制
6、lineType:表示绘制线轮廓时的线形
7、hierarchy:所输出的层次信息
8、maxLevel:控制轮廓层次的深度(如果是0表示绘制地0层轮廓)
9、offset:偏移参数使轮廓偏移一定位置用[ x , y ]坐标表示
"""
if cv2.contourArea(contours[i]) >= 0:
sum_all = sum_all + cv2.contourArea(contours[i])
print("第" + str(i + 1) + "个毛球的面积是:" + str(cv2.contourArea(contours[i])) + "个像素点")
self.ui.plainTextEdit.appendPlainText("第" + str(i + 1) + "个毛球的面积是:" + str(cv2.contourArea(contours[i])) + "个像素点")
#cv2.imshow("chulihou", draw_img)
# 键盘输入检测,参数为显示时长,如果参数为0就是键盘按下退出显示
cv2.waitKey()
# 关闭所有窗口
cv2.destroyAllWindows()
self.ui.label_8.setText(str(len(contours)))
self.ui.label_9.setText(str(sum_all))
self.ui.label_10.setText("1级")
# # 设置标签的图片
# self.ui.label_2.setPixmap(self.image_file) ##输入为图片路径,比如当前文件内的logo.png图片
# # self.label.setFixedSize(600, 400) # 设置显示固定尺寸,可以根据图片的像素长宽来设置
# self.ui.label_2.setScaledContents(True) # 让图片自适应 label 大小
if __name__=="__main__":
app = QApplication([])
gui = beyondyanyu() #初始化
gui.window_init()
gui.ui.show() #将窗口控件显示在屏幕上
app.exit(app.exec_())
