|
@@ -0,0 +1,2223 @@
|
|
|
+##############功能测试##################
|
|
|
+import ctypes
|
|
|
+from shapely.geometry import Polygon
|
|
|
+from shapely.ops import unary_union
|
|
|
+# whnd = ctypes.windll.kernel32.GetConsoleWindow()
|
|
|
+# if whnd != 0:
|
|
|
+# ctypes.windll.user32.ShowWindow(whnd, 0)
|
|
|
+# ctypes.windll.kernel32.CloseHandle(whnd)
|
|
|
+
|
|
|
+
|
|
|
+import sys
|
|
|
+import os
|
|
|
+import subprocess
|
|
|
+import time
|
|
|
+import cv2
|
|
|
+import shutil
|
|
|
+from pathlib import Path
|
|
|
+from split_train_val_test import split
|
|
|
+from crop import crop
|
|
|
+from json_to_yolo import json_to_yolo
|
|
|
+from datetime import datetime
|
|
|
+from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QPushButton, QWidget
|
|
|
+FILE = Path(__file__).resolve()
|
|
|
+ROOT = FILE.parents[0] # YOLOv5 root directory
|
|
|
+if str(ROOT) not in sys.path:
|
|
|
+ sys.path.append(str(ROOT)) # add ROOT to PATH
|
|
|
+ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
|
|
+from PyQt5.QtGui import QPixmap
|
|
|
+from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QButtonGroup, QMessageBox, QListView, \
|
|
|
+ QAbstractItemView, QTreeView, QWidget, QVBoxLayout
|
|
|
+from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal, QDateTime
|
|
|
+from qt_win.win3 import Ui_mainWindow, MessageBox, InfoMessageBox
|
|
|
+from PyQt5.uic import loadUi
|
|
|
+import apprcc_rc
|
|
|
+import glob
|
|
|
+from utils.segment.dataloaders import polygons2masks # 用来重新画修正后的图片
|
|
|
+from utils.plots import Annotator, colors
|
|
|
+from name import CT_name_zh, CT_name # 演示用coco 后续换成
|
|
|
+import json
|
|
|
+import numpy as np
|
|
|
+import math
|
|
|
+from PIL import Image, ImageDraw, ImageFont
|
|
|
+
|
|
|
+
|
|
|
+os.environ["GIT_PYTHON_REFRESH"] = "quiet"
|
|
|
+os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
|
|
|
+import psutil
|
|
|
+
|
|
|
+"""
|
|
|
+ 通过进程名杀死进程
|
|
|
+ taskkill /F /IM explorer.exe
|
|
|
+"""
|
|
|
+
|
|
|
+
|
|
|
+def kill_name(name):
|
|
|
+ pids = psutil.pids()
|
|
|
+ for pid in pids:
|
|
|
+ p = psutil.Process(pid)
|
|
|
+ if p.name() == name:
|
|
|
+ if os.name == 'nt':
|
|
|
+ cmd = 'taskkill /pid ' + str(pid) + ' /f'
|
|
|
+ try:
|
|
|
+ print(pid, 'killed')
|
|
|
+ os.system(cmd)
|
|
|
+ except Exception as e:
|
|
|
+ print(e)
|
|
|
+ elif os.name == 'posix':
|
|
|
+ # Linux系统
|
|
|
+ cmd = 'kill ' + str(pid)
|
|
|
+ try:
|
|
|
+ print(pid, 'killed')
|
|
|
+ os.system(cmd)
|
|
|
+ except Exception as e:
|
|
|
+ print(e)
|
|
|
+
|
|
|
+
|
|
|
+def component_polygon_area(poly):
|
|
|
+ """Compute the area of a component of a polygon.
|
|
|
+ Args:
|
|
|
+ x (ndarray): x coordinates of the component
|
|
|
+ y (ndarray): y coordinates of the component
|
|
|
+
|
|
|
+ Return:
|
|
|
+ float: the are of the component
|
|
|
+ """
|
|
|
+ # poly = poly.numpy()
|
|
|
+ x = poly[:, 0]
|
|
|
+ y = poly[:, 1]
|
|
|
+ return 0.5 * np.abs(
|
|
|
+ np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))) # np.roll 意即“滚动”,类似移位操作
|
|
|
+ # 注意这里的np.dot表示一维向量相乘
|
|
|
+
|
|
|
+
|
|
|
+def component_polygon_Circle(poly):
|
|
|
+ """Compute the area of a component of a polygon.
|
|
|
+
|
|
|
+ """
|
|
|
+ # poly = poly.numpy()
|
|
|
+ poly = poly[:, None, :]
|
|
|
+ # poly .shpae (n,1,2)
|
|
|
+ _, radius = cv2.minEnclosingCircle(poly)
|
|
|
+ return 2 * math.pi * radius
|
|
|
+
|
|
|
+
|
|
|
+def component_polygon_Circle_max(src, poly):
|
|
|
+ # Calculate the distances to the contour
|
|
|
+ box = cv2.boundingRect(poly[:, None, :])
|
|
|
+ x, y, w, h = box
|
|
|
+ raw_dist = np.empty(src.shape[:2])
|
|
|
+
|
|
|
+ mask = np.zeros(src.shape, dtype=np.int32)
|
|
|
+ cv2.fillPoly(mask, [poly.astype(np.int32)], color=(255, 255, 255)) # 在大图上画该类的所有检测多边形
|
|
|
+
|
|
|
+ for i in range(x, x + w, ):
|
|
|
+ for j in range(y, y + h, ):
|
|
|
+ if mask[j, i, 0] == 255:
|
|
|
+ raw_dist[j, i] = cv2.pointPolygonTest(poly[:, None:, ], (i, j), True)
|
|
|
+
|
|
|
+ # 获取最大值即内接圆半径,中心点坐标
|
|
|
+ _, maxVal, _, maxLoc = cv2.minMaxLoc(raw_dist)
|
|
|
+
|
|
|
+ return maxVal * 2, maxLoc
|
|
|
+
|
|
|
+
|
|
|
+det_img = None
|
|
|
+
|
|
|
+
|
|
|
+def plots_new_one(json_file, img_name, out_path, flag=True): # 计算缺陷信息并展示
|
|
|
+ """
|
|
|
+ 画单张图片 默认是未纠错模式
|
|
|
+ """
|
|
|
+ s_t = time.time()
|
|
|
+ # print("s_t: ", s_t)
|
|
|
+ im0 = cv2.imread(img_name) # BGR
|
|
|
+ s2_t = time.time()
|
|
|
+ # print("time2: ", s2_t-s_t)
|
|
|
+ # Mask plotting
|
|
|
+ segments = []
|
|
|
+
|
|
|
+ class_id = []
|
|
|
+ class_name = []
|
|
|
+ # ccn = {}
|
|
|
+ new_dict = {v: k for k, v in CT_name.items()}
|
|
|
+ # print("plots_new_one, json_file: ",json_file)
|
|
|
+ if os.path.exists(json_file):
|
|
|
+ with open(json_file, 'r') as f:
|
|
|
+ data = json.load(f)
|
|
|
+ class_number = data['shapes'] # 类型个数
|
|
|
+
|
|
|
+ for cn in class_number:
|
|
|
+ if cn['label'] in list(CT_name_zh.keys()):
|
|
|
+ # segments.append(np.array(cn['points'],dtype=np.float32)) #获取每个轮廓点
|
|
|
+ segments.append(np.array(cn['points'], dtype=np.int32))
|
|
|
+ class_name.append(CT_name_zh[cn['label']]) # 获取对应的class name
|
|
|
+ class_id.append(new_dict[cn['label']]) # 获取对应的class id
|
|
|
+ # bboxs.append(np.array(cn['bbox'])) #x[0] y[0] 左上角点 用于画图 和显示信息
|
|
|
+ # ccn = data
|
|
|
+ # print("segments: ", segments[0].dtype)
|
|
|
+
|
|
|
+ s3_t = time.time()
|
|
|
+ # print("time3: ", s3_t - s2_t)
|
|
|
+ nc = len(class_name)
|
|
|
+ if nc > 0:
|
|
|
+ s3_1_t = time.time()
|
|
|
+ # print("time3_1: ", s3_1_t - s2_t)
|
|
|
+ annotator = Annotator(im0, line_width=1, example=str(CT_name)) #
|
|
|
+
|
|
|
+ # 二值化
|
|
|
+ # masks = polygons2masks(im0.shape[:2], segments, color=1)
|
|
|
+ s3_2_t = time.time()
|
|
|
+ # print("time3_2: ", s3_2_t - s3_1_t)
|
|
|
+ # 重新由轮廓点 求取信息 因为有可能更新
|
|
|
+
|
|
|
+ p_co = [colors(x, True) for x in class_id]
|
|
|
+
|
|
|
+ # annotator.masks_cpu( #cpu上
|
|
|
+ # masks,
|
|
|
+ # colors=p_co,
|
|
|
+ # im_gpu=im0.transpose(2, 0, 1) / 255)
|
|
|
+
|
|
|
+ s3_3_t = time.time()
|
|
|
+ # print("time3_3: ", s3_3_t - s3_2_t)
|
|
|
+ # im0 = annotator.result()
|
|
|
+
|
|
|
+ s4_t = time.time()
|
|
|
+ # print("time4: ", s4_t - s3_t)
|
|
|
+
|
|
|
+ alpha = 0.75
|
|
|
+ mask = np.zeros((im0.shape[0], im0.shape[1]), dtype=np.uint8)
|
|
|
+ overlay = im0.copy()
|
|
|
+
|
|
|
+ cv2.fillPoly(mask, pts=segments, color=(255, 255, 255))
|
|
|
+ cv2.fillPoly(im0, pts=segments, color=(125, 255, 0))
|
|
|
+ cv2.addWeighted(overlay, alpha, im0, 1 - alpha, 0, im0)
|
|
|
+ print("flag: ", flag)
|
|
|
+ for i in range(nc):
|
|
|
+ txt_color = p_co[i] # cocoors(new_dict[list(ccn.keys())[i]], True)
|
|
|
+ # x, y, _, _ = cv2.boundingRect(segments[i][:,None:,])
|
|
|
+ # print("segments: ",segments[i].shape) #(754, 2)
|
|
|
+ xy_index = np.argmin(segments[i], axis=0) # 其中,axis=1表示按行计算
|
|
|
+ # print("xy_index.shape,xy_index: ",xy_index.shape,xy_index) #(2,) [ 139.55 267.14]
|
|
|
+ textSize = int(0.04 * im0.shape[0])
|
|
|
+
|
|
|
+ x = segments[i][xy_index[1], 0]
|
|
|
+ y = segments[i][xy_index[1], 1]
|
|
|
+ if y - textSize <= 10:
|
|
|
+ y = 10
|
|
|
+
|
|
|
+ # print("im0.shape[0]: ", im0.shape[0], x, y)
|
|
|
+
|
|
|
+ def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=0.04):
|
|
|
+ # print("textColor: ", type(textColor), tuple(list(textColor)[::-1]))
|
|
|
+ textColor = tuple(list(textColor)[::-1])
|
|
|
+ if (isinstance(img, np.ndarray)): # 判断是否OpenCV图片类型
|
|
|
+ img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
|
|
|
+ # print("img.size: ", img.size)
|
|
|
+ # 创建一个可以在给定图像上绘图的对象
|
|
|
+ draw = ImageDraw.Draw(img)
|
|
|
+ # 字体的格式
|
|
|
+ # fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")
|
|
|
+ fontStyle = ImageFont.truetype("simhei.ttf", textSize, encoding="utf-8")
|
|
|
+ # 绘制文本
|
|
|
+ draw.text(position, text, textColor, font=fontStyle)
|
|
|
+ # 转换回OpenCV格式
|
|
|
+ return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
|
|
|
+ # cv2.putText(im0,class_name[i],(int(x), int(y)), 0, 0.75, txt_color, thickness=2 ,lineType=cv2.LINE_AA)
|
|
|
+
|
|
|
+ if not flag:
|
|
|
+ pass
|
|
|
+ # im0 = cv2AddChineseText(im0, f"{class_name[i]}", (int(x), int(y)), txt_color, textSize)
|
|
|
+
|
|
|
+ else:
|
|
|
+ # 计算面积
|
|
|
+ area = round(cv2.contourArea(segments[i]), 2)
|
|
|
+ lenth = round(cv2.arcLength(segments[i], True) / 2, 2)
|
|
|
+ # 计算宽度
|
|
|
+ width, maxLoc = component_polygon_Circle_max(im0, segments[i])
|
|
|
+ im0 = cv2AddChineseText(im0, f"{class_name[i]}({int(area)}mm²,{int(lenth)}mm,{int(width)}mm)",
|
|
|
+ (int(x), int(y + 10)), txt_color, textSize) # 只显示面积
|
|
|
+
|
|
|
+ s5_t = time.time()
|
|
|
+ print("time5: ", s5_t - s4_t)
|
|
|
+
|
|
|
+ # global det_img
|
|
|
+ # det_img=im0
|
|
|
+ cv2.imwrite(os.path.join(out_path, os.path.basename(img_name)), im0)
|
|
|
+
|
|
|
+
|
|
|
+# labelme 2 yolov5
|
|
|
+def convert_json_label_to_yolov_seg_label(json_file, root_path, save_path):
|
|
|
+ # print(json_file)
|
|
|
+ f = open(json_file)
|
|
|
+ json_info = json.load(f)
|
|
|
+ # print(json_info.keys())
|
|
|
+ img = cv2.imread(os.path.join(root_path, json_info["imagePath"]))
|
|
|
+ height, width, _ = img.shape
|
|
|
+ np_w_h = np.array([[width, height]], np.int32)
|
|
|
+ txt_file = save_path # json_file.replace(".json", ".txt")
|
|
|
+ f = open(txt_file, "a")
|
|
|
+ for point_json in json_info["shapes"]:
|
|
|
+ txt_content = ""
|
|
|
+ np_points = np.array(point_json["points"], np.int32)
|
|
|
+ norm_points = np_points / np_w_h
|
|
|
+ norm_points_list = norm_points.tolist()
|
|
|
+ txt_content += "0 " + " ".join([" ".join([str(cell[0]), str(cell[1])]) for cell in norm_points_list]) + "\n"
|
|
|
+ f.write(txt_content)
|
|
|
+
|
|
|
+
|
|
|
+class TrainThread(QThread): # 调用train_qt.py
|
|
|
+ send_msg = pyqtSignal(str) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
|
|
|
+
|
|
|
+ # send_percent = pyqtSignal(int) #
|
|
|
+ def __init__(self):
|
|
|
+ super(TrainThread, self).__init__()
|
|
|
+ self.model = " "
|
|
|
+ self.Work = ["python.exe", "train_qt.py"] # os后台执行命令行 内容
|
|
|
+ self.Command = []
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def getAllImage(self, folderPath, imageList):
|
|
|
+ extend_name = ["jpg", "jpeg", "png", "bmp", "tif", "tiff"]
|
|
|
+ # exclude_dir=["data_crop","data_yolo","data_original"]
|
|
|
+ # exclude_dir = ["data_crop", "data_yolo", "data_debug","val","eval","eval_debug"]
|
|
|
+ if os.path.isfile(folderPath):
|
|
|
+ basename = os.path.basename(folderPath)
|
|
|
+ ext = basename.rsplit(".")[-1]
|
|
|
+ bsname = basename.rsplit(".", 1)[0]
|
|
|
+ len1 = len(folderPath) - len(ext)
|
|
|
+ json_f = folderPath[:len1] + "json"
|
|
|
+
|
|
|
+ if ext in extend_name:
|
|
|
+ if os.path.exists(json_f):
|
|
|
+ imageList.append(folderPath)
|
|
|
+
|
|
|
+ return imageList
|
|
|
+ else:
|
|
|
+ for item in os.listdir(folderPath):
|
|
|
+ subFolderPath = os.path.join(folderPath, item)
|
|
|
+ self.getAllImage(subFolderPath, imageList)
|
|
|
+ return imageList
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ try:
|
|
|
+ self.send_msg.emit('开始训练')
|
|
|
+ # 先准备好yolo格式的数据集
|
|
|
+ data_paths = self.Command[-1]
|
|
|
+ print("data_paths: ", data_paths)
|
|
|
+
|
|
|
+ imageList = []
|
|
|
+ data_paths_ = data_paths.strip().split(";")
|
|
|
+ print("data_paths_: ", data_paths_)
|
|
|
+ for data_path in data_paths_:
|
|
|
+ if len(data_path) > 0:
|
|
|
+ self.getAllImage(data_path, imageList) # 获取所有原始大图
|
|
|
+ print("imageList: ", imageList)
|
|
|
+
|
|
|
+ split(imageList) # 划分train-val-test
|
|
|
+ crop()
|
|
|
+ json_to_yolo()
|
|
|
+ info = self.Work
|
|
|
+ if self.Command[0] == "True":
|
|
|
+ info += ["--weights"] + [self.model]
|
|
|
+ info += ["--hyp"] + [self.Command[-3]] + ["--imgsz"] + [self.Command[1]] \
|
|
|
+ + ["--epochs"] + [self.Command[2]] + ["--batch-size"] + [self.Command[3]] \
|
|
|
+ + ["--ckptname"] + [self.Command[-4]] + ["--project"] + [self.Command[-2]]
|
|
|
+ # print(info)
|
|
|
+
|
|
|
+ # os.system(info)
|
|
|
+ subprocess.run(info)
|
|
|
+ self.send_msg.emit('训练结束')
|
|
|
+ # self.statistic_label.clear()
|
|
|
+ except Exception as e:
|
|
|
+ print('开始训练 %s' % e)
|
|
|
+ self.send_msg.emit('%s' % e)
|
|
|
+
|
|
|
+# 识别
|
|
|
+class DetThread(QThread): # 检测类,调用predict_largepic.py
|
|
|
+ send_msg = pyqtSignal(str) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ super(DetThread, self).__init__()
|
|
|
+
|
|
|
+ self.model = ""
|
|
|
+ self.Work = ["python.exe", "predict_qt.py"] # os后台执行命令行 内容
|
|
|
+ self.Work2 = ["python.exe", "predict_largepic.py"] # os后台执行命令行 内容
|
|
|
+
|
|
|
+ self.data = None # 默认
|
|
|
+ self.split = False # 默认 不 切片
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ try:
|
|
|
+ if self.split:
|
|
|
+ info = self.Work2 + ["--batch-size"] + [self.Command[3]]
|
|
|
+ else:
|
|
|
+ info = self.Work
|
|
|
+ info += ["--weights"] + [self.model]
|
|
|
+ info += ["--imgsz"] + [self.Command[0]] \
|
|
|
+ + ["--conf-thres"] + [self.Command[2]] + ["--batch-size"] + ["1"] \
|
|
|
+ + ["--iou-thres"] + [self.Command[1]] + ["--project"] + [self.Command[-1]]
|
|
|
+ if self.data:
|
|
|
+ info += ["--source"] + [self.data]
|
|
|
+ # print(info)
|
|
|
+ self.send_msg.emit('开始检测')
|
|
|
+ print("*********************************************************")
|
|
|
+ print("DetThread info: ", info)
|
|
|
+ # os.system(info)
|
|
|
+ # subprocess.Popen(info) #这样不阻塞
|
|
|
+ subprocess.run(info) # 这样阻塞
|
|
|
+
|
|
|
+ self.send_msg.emit('检测结束')
|
|
|
+ # self.statistic_label.clear()
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ print('%s' % e)
|
|
|
+ self.send_msg.emit('%s' % e)
|
|
|
+
|
|
|
+
|
|
|
+class FeaThread(QThread):
|
|
|
+ send_msg = pyqtSignal(str) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ super().__init__()
|
|
|
+ self.py = ""
|
|
|
+ self.data = None
|
|
|
+ #
|
|
|
+ # def run(self):
|
|
|
+ # self.send_msg.emit("s")
|
|
|
+
|
|
|
+
|
|
|
+class JianThread(QThread):
|
|
|
+ send_msg = pyqtSignal(str) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ super().__init__()
|
|
|
+ self.py = ""
|
|
|
+ self.data = None
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+class LabelThread(QThread): # 调用labelme第三方库
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ super(LabelThread, self).__init__()
|
|
|
+ # labelme 必须找到入口文件
|
|
|
+ self.Work = "python.exe D:\\Anaconda3\\envs\\pyqt\\Lib\site-packages\\labelme\\__main__.py --labels ./labels.txt" # ./res/ --labels ./dataset/labels.txt" # os后台执行命令行 内容
|
|
|
+ self.Command = []
|
|
|
+ self.src_path = "" # 是检测图片所在路径,也就是标注文件所在路径
|
|
|
+ self.det_path = "" # 是检测结果json文件的路径
|
|
|
+ self.cor_path = "D:/data/correct"
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ try:
|
|
|
+ info = self.Work
|
|
|
+ info += " " + self.src_path
|
|
|
+ # print(info)
|
|
|
+ # copy 检测结果的 至
|
|
|
+
|
|
|
+ max_i = 0
|
|
|
+ if not os.path.exists(self.cor_path):
|
|
|
+ os.mkdir(self.cor_path)
|
|
|
+ max_i = 0
|
|
|
+
|
|
|
+ else:
|
|
|
+ cor_json_list = glob.glob(self.cor_path + '/*.json') # 再将现在检测好的json文件移到图片所在文件夹下
|
|
|
+ if len(cor_json_list) == 0:
|
|
|
+ max_i = 0
|
|
|
+ else:
|
|
|
+ cor_json_list = sorted(cor_json_list, key=lambda x: int(os.path.basename(x)[:-5]),
|
|
|
+ reverse=True) # 降序
|
|
|
+ print("cor_json_list: ", cor_json_list)
|
|
|
+ max_i = int((os.path.basename(cor_json_list[0])[:-5]))
|
|
|
+
|
|
|
+ print("max_i: ", max_i)
|
|
|
+
|
|
|
+ json_list = glob.glob(self.det_path + '/*.json') # 再将现在检测好的json文件移到图片所在文件夹下
|
|
|
+ # 记录下检测文件的修改时间
|
|
|
+ time_d = {}
|
|
|
+ for jsons in json_list:
|
|
|
+ name = os.path.basename(jsons)
|
|
|
+ # if os.path.exists(os.path.join(self.src_path, name)):
|
|
|
+ # shutil.move(os.path.join(self.src_path, name), os.path.join(self.src_path, "gt_"+name)) #避免检测结果的json文件覆盖掉标注的json文件
|
|
|
+ shutil.copyfile(jsons, os.path.join(self.src_path,
|
|
|
+ name)) # 把检测结果json文件复制到检测图像所在文件夹下,这不就是用检测的json文件替代了之前的标注文件吗?不能这么操作啊!这种方法只能适用于检测图片没有标签文件的情况
|
|
|
+ time_d[name] = os.path.getmtime(os.path.join(self.src_path, name)) # 修改时间
|
|
|
+ # print("info: ", info) #python.exe C:/Users/ma/anaconda3/Lib/site-packages/labelme/__main__.py --labels ./labels.txt D:/data/data_original/20220516
|
|
|
+ os.system(info)
|
|
|
+
|
|
|
+ # 将在检测图片所在文件夹下的手工修正好的文件替换回去,代替之前的检测文件
|
|
|
+ classes_dict = {"crack": 0, "hole": 1, "debonding": 2, "rarefaction": 3,
|
|
|
+
|
|
|
+ "black_crack": 0, "black_hole": 1, "black_debonding": 2,
|
|
|
+ "white_crack": 0, "white_hole": 1, "white_debonding": 2,
|
|
|
+ "白色裂纹": 0, "白裂纹": 0, "裂纹": 0, "白色裂缝": 0, "白裂缝": 0, "裂缝": 0,
|
|
|
+ "白色裂隙": 0, "白裂隙": 0, "裂隙": 0,
|
|
|
+ "黑色裂纹": 0, "黑裂纹": 0, "黑色裂缝": 0, "黑裂缝": 0, "黑色裂隙": 0, "黑裂隙": 0,
|
|
|
+ "白色孔洞": 1, "白孔洞": 1, "孔洞": 1, "黑色孔洞": 1, "黑孔洞": 1,
|
|
|
+ "白色脱粘": 2, "白脱粘": 2, "脱粘": 2, "黑色脱粘": 2, "黑脱粘": 2,
|
|
|
+ "疏松": 3} # 将各种叫法全合并为0、1、2、3四类
|
|
|
+ json_list = glob.glob(self.src_path + '/*.json')
|
|
|
+ for jsons in json_list:
|
|
|
+ name = os.path.basename(jsons)
|
|
|
+
|
|
|
+ changed = False
|
|
|
+ # 旧json文件的修改时间
|
|
|
+ if name not in time_d: # 运行这个if说明在图片文件夹下新增了json文件,这是用户纠错时添加的新json文件,内容是漏检的缺陷,需要拷贝回runs/detect文件夹下
|
|
|
+ max_i += 1
|
|
|
+ print("新增的json文件,复制到cor_path下:", os.path.join(self.cor_path, str(max_i) + ".json"))
|
|
|
+ shutil.copyfile(jsons, os.path.join(self.det_path, name))
|
|
|
+ shutil.move(jsons, os.path.join(self.cor_path, str(max_i) + ".json")) # 同时把新增的json文件命名为cor文件
|
|
|
+ extenxion = ['.jpg', '.png', '.bmp', '.jpeg', '.tif', '.tiff']
|
|
|
+ for ext in extenxion:
|
|
|
+ imgf = (jsons[:-5] + ext)
|
|
|
+ if os.path.exists(imgf):
|
|
|
+ shutil.copyfile(imgf, os.path.join(self.cor_path, str(max_i) + ext))
|
|
|
+ break
|
|
|
+
|
|
|
+ continue
|
|
|
+ old_t = time_d[name]
|
|
|
+
|
|
|
+ new_t = os.path.getmtime(os.path.join(self.src_path, name))
|
|
|
+ # new_t = os.path.getmtime(os.path.join(self.src_path, name))
|
|
|
+ print("name, 时间差: ", name, new_t - old_t)
|
|
|
+ print("修改时间相等吗:", (new_t - old_t) < 1)
|
|
|
+ if (new_t - old_t) < 1: # 这个if成立了就说明新旧json文件的修改时间没变,删除src_path下的该json文件
|
|
|
+ print("修改时间相等,删除:", os.path.join(self.src_path, name))
|
|
|
+ os.remove(os.path.join(self.src_path, name))
|
|
|
+ continue
|
|
|
+ else: # 光修改时间变了不行,还要看里面的多边形是否变了
|
|
|
+ old_masks = [[], [], [], []]
|
|
|
+ new_masks = [[], [], [], []]
|
|
|
+
|
|
|
+ # 找旧的masks
|
|
|
+ with open(os.path.join(self.det_path, name)) as f:
|
|
|
+
|
|
|
+ temp_shapes = json.loads(f.read())["shapes"]
|
|
|
+ for old_polygon_temp in temp_shapes:
|
|
|
+ old_masks[classes_dict[old_polygon_temp["label"]]].append(
|
|
|
+ Polygon(old_polygon_temp["points"]))
|
|
|
+
|
|
|
+ # 找新的masks
|
|
|
+ with open(os.path.join(self.src_path, name)) as f:
|
|
|
+
|
|
|
+ temp_shapes = json.loads(f.read())["shapes"]
|
|
|
+ for new_polygon_temp in temp_shapes:
|
|
|
+ new_masks[classes_dict[new_polygon_temp["label"]]].append(
|
|
|
+ Polygon(new_polygon_temp["points"]))
|
|
|
+
|
|
|
+ # print("src old_masks: ",name,old_masks)
|
|
|
+ # print("out new_masks: ", name,new_masks)
|
|
|
+
|
|
|
+ for i in range(len(old_masks)): # 首先看下新旧缺陷的每一类缺陷的数量是否完全相等,不相等就是改变了
|
|
|
+ if len(old_masks[i]) != len(new_masks[i]):
|
|
|
+ changed = True
|
|
|
+ break
|
|
|
+ print("缺陷数量比较结果 changed: ", changed)
|
|
|
+ if changed == False: # 这个if成立了就说明新旧缺陷的数量完全相等,接下来就该比较新旧缺陷的iou了
|
|
|
+ h, w = 5000, 5000
|
|
|
+ for ext in ["jpg", "jpeg", "tif", "tiff", "png", "bmp"]:
|
|
|
+ img_path = os.path.join(self.src_path, name[:-5] + "." + ext)
|
|
|
+ # print("img_path: ",img_path)
|
|
|
+ if os.path.exists(img_path):
|
|
|
+ img = cv2.imread(img_path)
|
|
|
+ h, w, _ = img.shape
|
|
|
+ break
|
|
|
+ # print("h,w: ",h,w)
|
|
|
+ for i in range(len(old_masks)):
|
|
|
+ for old_mask in old_masks[i]:
|
|
|
+ match = False
|
|
|
+ for new_mask in new_masks[i]:
|
|
|
+ # old_area=old_mask.area
|
|
|
+ # new_area=new_mask.area
|
|
|
+
|
|
|
+ # 计算交集
|
|
|
+ mask_gt = np.zeros([h, w, 3], dtype=np.int32) # 假定一个最大尺寸的空白图片,画真值框对应的区域
|
|
|
+ ps = (list(old_mask.exterior.coords))
|
|
|
+ ps = np.array(ps, dtype=np.int32)
|
|
|
+ ps = ps.reshape(-1, 2)
|
|
|
+
|
|
|
+ # f = open(dir + "/whole_" + task + "_" + str(i) + "_" + cls_names[i] + ".txt", "w")
|
|
|
+ cv2.fillPoly(mask_gt, [ps], color=(125, 125, 125)) # 给真值框对应的空白图片按真值框的点集坐标上色
|
|
|
+ gray = cv2.cvtColor(mask_gt.astype(np.float32), cv2.COLOR_BGR2GRAY)
|
|
|
+ old_area = np.sum(gray == 125)
|
|
|
+ # ret, thresh = cv2.threshold(gray, 125 - 1, 255, cv2.THRESH_BINARY)
|
|
|
+ # contours, _ = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE,
|
|
|
+ # cv2.CHAIN_APPROX_SIMPLE)
|
|
|
+ #
|
|
|
+ # old_area = 0
|
|
|
+ # for c in contours: # 重叠区域可能有多个,把所有的面积加起来
|
|
|
+ # area = cv2.contourArea(c, oriented=False)
|
|
|
+ # old_area += area
|
|
|
+
|
|
|
+ mask_pred = np.zeros([h, w, 3], dtype=np.int32)
|
|
|
+ ps = (list(new_mask.exterior.coords))
|
|
|
+ ps = np.array(ps, dtype=np.int32)
|
|
|
+ ps = ps.reshape(-1, 2)
|
|
|
+
|
|
|
+ # f = open(dir + "/whole_" + task + "_" + str(i) + "_" + cls_names[i] + ".txt", "w")
|
|
|
+ cv2.fillPoly(mask_pred, [ps], color=(125, 125, 125))
|
|
|
+ gray = cv2.cvtColor(mask_pred.astype(np.float32), cv2.COLOR_BGR2GRAY)
|
|
|
+ new_area = np.sum(gray == 125)
|
|
|
+ # ret, thresh = cv2.threshold(gray, 125 - 1, 255, cv2.THRESH_BINARY)
|
|
|
+ # contours, _ = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE,
|
|
|
+ # cv2.CHAIN_APPROX_SIMPLE)
|
|
|
+ #
|
|
|
+ # new_area = 0
|
|
|
+ # for c in contours: # 重叠区域可能有多个,把所有的面积加起来
|
|
|
+ # area = cv2.contourArea(c, oriented=False)
|
|
|
+ # new_area += area
|
|
|
+
|
|
|
+ gray = cv2.cvtColor((mask_pred + mask_gt).astype(np.float32),
|
|
|
+ cv2.COLOR_BGR2GRAY) # 如果真值点集和预测点集有重叠区域,就对重叠区域计算轮廓面积
|
|
|
+ inter_a = np.sum(gray == 125 * 2)
|
|
|
+ # ret, thresh = cv2.threshold(gray, 250 - 1, 255, cv2.THRESH_BINARY)
|
|
|
+ # contours, _ = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE,
|
|
|
+ # cv2.CHAIN_APPROX_SIMPLE)
|
|
|
+ #
|
|
|
+ # inter_a = 0
|
|
|
+ # for c in contours: # 重叠区域可能有多个,把所有的面积加起来
|
|
|
+ # area = cv2.contourArea(c, oriented=False)
|
|
|
+ # inter_a += area
|
|
|
+
|
|
|
+ iou = inter_a / (old_area + new_area - inter_a)
|
|
|
+ # iou = self.poly_iou(old_mask, new_mask)
|
|
|
+ # print("i, iou, old_area,new_area,inter_a,h,w: ", i, iou,old_area,new_area,inter_a,h,w)
|
|
|
+ if iou > 0.95: # 如果old mask在new mask里能找到匹配,说明用户没有修old maks,就接着变量其他old mask
|
|
|
+ match = True
|
|
|
+ break
|
|
|
+ if match == False: # 如果new mask遍历完了,还是没匹配上,说明old mask被用户改变了,此时changed置为True,并不再遍历其他old mas!
|
|
|
+ changed = True
|
|
|
+ break
|
|
|
+ if changed == True: # 如果changed被置为True,就不再遍历其他类别的缺陷了,直接退出
|
|
|
+ break
|
|
|
+ if changed == True:
|
|
|
+ max_i += 1
|
|
|
+ print("json文件内容改变了,复制到cor_path下:", os.path.join(self.cor_path, str(max_i) + ".json"))
|
|
|
+ shutil.copyfile(jsons, os.path.join(self.det_path, name))
|
|
|
+ shutil.move(jsons, os.path.join(self.cor_path, str(max_i) + ".json"))
|
|
|
+ extenxion = ['.jpg', '.png', '.bmp', '.jpeg', '.tif', '.tiff']
|
|
|
+ for ext in extenxion:
|
|
|
+ imgf = (jsons[:-5] + ext)
|
|
|
+ if os.path.exists(imgf):
|
|
|
+ shutil.copyfile(imgf, os.path.join(self.cor_path, str(max_i) + ext))
|
|
|
+ break
|
|
|
+
|
|
|
+ else:
|
|
|
+ print("json文件没变,删除src_path下的json文件")
|
|
|
+ os.remove(jsons)
|
|
|
+ print("最终的changed: ", changed)
|
|
|
+ print('close labelme!')
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ print('出错了:%s' % e)
|
|
|
+
|
|
|
+ def poly_iou(self, poly1: Polygon, poly2: Polygon):
|
|
|
+ intersection_area = poly1.intersection(poly2).area
|
|
|
+ union_area = poly1.union(poly2).area
|
|
|
+ return intersection_area / union_area
|
|
|
+
|
|
|
+
|
|
|
+class LoggerWindow():
|
|
|
+
|
|
|
+ def __init__(self, mode='detect', path='') -> None:
|
|
|
+ super().__init__()
|
|
|
+ self.mode = mode # 训练还是检测日志窗口
|
|
|
+
|
|
|
+ self.path = path # 读取日志文件
|
|
|
+
|
|
|
+ self.ui = loadUi("qt_win/lx.ui") # 添加组件
|
|
|
+
|
|
|
+ self.ui.label.setText(mode)
|
|
|
+
|
|
|
+ # 读取文件
|
|
|
+ f = open(path, 'r', encoding='utf-8')
|
|
|
+ data = f.read()
|
|
|
+ f.close()
|
|
|
+ self.ui.textBrowser.append(data)
|
|
|
+
|
|
|
+
|
|
|
+class PlotThread(QThread): # 调用plots_new_one函数
|
|
|
+ send_msg = pyqtSignal(str) # 信号
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ super(PlotThread, self).__init__()
|
|
|
+ self.json_file = ""
|
|
|
+ self.img_name = ""
|
|
|
+ self.out_path = ""
|
|
|
+ self.flag = False
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ try:
|
|
|
+ self.send_msg.emit("开始显示结果")
|
|
|
+ # print("PlotThread.run(): self.json_file,self.img_name,self.out_path,self.flag: ", self.json_file,self.img_name,self.out_path,self.flag)
|
|
|
+ plots_new_one(self.json_file, self.img_name, self.out_path, self.flag)
|
|
|
+ self.send_msg.emit("显示结果完成")
|
|
|
+ except Exception as e:
|
|
|
+ print('PlotThread Exception %s' % e)
|
|
|
+ self.send_msg.emit("error")
|
|
|
+
|
|
|
+
|
|
|
+def write_xlsxwriter_new(sys, img_list, file_path, src_path, gsd):
|
|
|
+ import xlsxwriter # 对excel数据进行写入的库
|
|
|
+ # 新建工作簿
|
|
|
+ writebook = xlsxwriter.Workbook(file_path) # 生成excel文件并设置编码为utf8
|
|
|
+ sheet1 = writebook.add_worksheet() # 创建第一个sheet 表单
|
|
|
+
|
|
|
+ # 注意:在 XlsxWriter 中,行和列都是零索引。第一个单元格 A1 是 (0, 0)。
|
|
|
+ # 设置列宽
|
|
|
+ sheet1.set_default_row(20)
|
|
|
+ # sheet1.set_default_row(40)
|
|
|
+ column_text_wrap = writebook.add_format()
|
|
|
+ column_text_wrap.set_text_wrap()
|
|
|
+ sheet1.set_column('A:A', 15)
|
|
|
+ sheet1.set_column('B:B', 15)
|
|
|
+ sheet1.set_column('E:E', 25, column_text_wrap)
|
|
|
+ sheet1.set_column('F:F', 25, column_text_wrap)
|
|
|
+ sheet1.set_column('K:K', 15)
|
|
|
+
|
|
|
+ bold = writebook.add_format({
|
|
|
+ 'bold': True, # 字体加粗
|
|
|
+ 'border': 1, # 单元格边框宽度
|
|
|
+ 'align': 'center',
|
|
|
+ 'valign': 'vcentre', # 字体对齐方式
|
|
|
+ 'text_wrap': True, # 是否自动换行
|
|
|
+ })
|
|
|
+ soft = writebook.add_format({
|
|
|
+ 'bold': False, # 字体加粗
|
|
|
+ 'border': 1, # 单元格边框宽度
|
|
|
+ 'align': 'center',
|
|
|
+ 'valign': 'vcentre', # 字体对齐方式
|
|
|
+ 'text_wrap': True, # 是否自动换行
|
|
|
+ })
|
|
|
+ # title
|
|
|
+ sheet1.merge_range(0, 0, 0, 16, sys, bold)
|
|
|
+ sheet1.merge_range(1, 0, 1, 1, "图像名称", bold)
|
|
|
+ sheet1.merge_range(1, 2, 1, 3, "缺陷类型", bold)
|
|
|
+ sheet1.merge_range(1, 4, 1, 5, "缺陷位置(像素)", bold)
|
|
|
+ sheet1.merge_range(1, 6, 1, 7, "缺陷面积(mm²)", bold)
|
|
|
+ sheet1.merge_range(1, 8, 1, 9, "缺陷长度(mm)", bold)
|
|
|
+ sheet1.merge_range(1, 10, 1, 11, "缺陷宽度(等效圆直径)(mm)", bold)
|
|
|
+ sheet1.merge_range(1, 12, 1, 16, "检测图像", bold)
|
|
|
+ # 加入内容
|
|
|
+ start = 2
|
|
|
+ print("img_list: ", len(img_list), img_list)
|
|
|
+ for img_f in img_list:
|
|
|
+ # 读取json文件内容,返回字典格式
|
|
|
+ img_n = img_f.split('.')[-1]
|
|
|
+ basename = os.path.basename(img_f)
|
|
|
+ js = os.path.join(src_path, 'jsons', basename.replace(img_n, 'json'))
|
|
|
+ if not os.path.exists(js):
|
|
|
+ print("生成报表时 not exists json: ", js)
|
|
|
+ continue
|
|
|
+ with open(js, 'r', encoding='utf-8') as fp:
|
|
|
+ json_data = json.load(fp)
|
|
|
+ class_number = len(json_data['shapes']) # 类型个数
|
|
|
+ # print("img_f, class_number: ", img_f, class_number)
|
|
|
+ if class_number == 0:
|
|
|
+ print("img_f, class_number==0: ", img_f, class_number)
|
|
|
+ continue
|
|
|
+ last = start + class_number - 1
|
|
|
+ # first_row, first_col, last_row, last_col
|
|
|
+ sheet1.merge_range(start, 0, last, 1, img_f, soft)
|
|
|
+
|
|
|
+ neirong = json_data['shapes']
|
|
|
+
|
|
|
+ # 加图 -- 判断是否存在 没有就重新画图
|
|
|
+ # name = os.path.basename(img_f)
|
|
|
+
|
|
|
+ if not os.path.exists(os.path.join(src_path, basename)):
|
|
|
+ plots_new_one(js, img_f, os.path.join(src_path), False)
|
|
|
+ img = cv2.imread(os.path.join(src_path, basename))
|
|
|
+ h, w, _ = img.shape
|
|
|
+ # row, col
|
|
|
+
|
|
|
+ if class_number <= 1:
|
|
|
+ nn = 1
|
|
|
+ scale_x, scale_y = 300 / w, (25 * nn) / h
|
|
|
+ else:
|
|
|
+ nn = class_number
|
|
|
+ scale_x, scale_y = 300 / w, (25 * nn) / h
|
|
|
+ bsname, ext_old = basename.rsplit(".", 1)
|
|
|
+ is_tif = False
|
|
|
+ for ext in ["tif", "tiff"]:
|
|
|
+ if ext_old == ext:
|
|
|
+ new_basename = bsname + "_forxlsx.png"
|
|
|
+ cv2.imwrite(os.path.join(src_path, new_basename), img)
|
|
|
+
|
|
|
+ # shutil.copyfile(os.path.join(src_path, basename),os.path.join(src_path, new_basename))
|
|
|
+ sheet1.insert_image(start, 12, os.path.join(src_path, new_basename),
|
|
|
+ {'x_scale': scale_x, 'y_scale': scale_y, 'positioning': 1})
|
|
|
+ print("insert success :", os.path.join(src_path, basename), os.path.join(src_path, new_basename))
|
|
|
+ # os.remove(os.path.join(src_path, new_basename))
|
|
|
+ is_tif = True
|
|
|
+ break
|
|
|
+ if is_tif == False:
|
|
|
+ sheet1.insert_image(start, 12, os.path.join(src_path, basename),
|
|
|
+ {'x_scale': scale_x, 'y_scale': scale_y, 'positioning': 1})
|
|
|
+
|
|
|
+ for j in range(class_number):
|
|
|
+ # 重新计算
|
|
|
+ sheet1.merge_range(start + j, 2, start + j, 3, CT_name_zh[neirong[j]['label']], soft)
|
|
|
+
|
|
|
+ segment = np.array(neirong[j]['points'], dtype=np.float32) # 获取每个轮廓点
|
|
|
+ # print("segment,segment.shape: ",segment,segment.shape)
|
|
|
+ # print("segment x: ",segment[:,0],np.mean(segment[:,0]))
|
|
|
+ # print("segment y: ", segment[:, 1],np.mean(segment[:,1])
|
|
|
+
|
|
|
+ if neirong[j]['label'] in ["hole", "rarefaction"]:
|
|
|
+ area = round(cv2.contourArea(segment[:, None, :]) * gsd * gsd, 1)
|
|
|
+ width = round(math.sqrt(4 * area / math.pi), 1)
|
|
|
+ lenth = "-"
|
|
|
+ else:
|
|
|
+ area = "-"
|
|
|
+ lenth = round(cv2.arcLength(segment[:, None:, ], True) / 2, 2)
|
|
|
+ lenth = round(lenth * gsd)
|
|
|
+ # 计算宽度
|
|
|
+ width, _ = component_polygon_Circle_max(img, segment)
|
|
|
+ width = round(width * gsd)
|
|
|
+
|
|
|
+ cx, cy = np.around(np.mean(segment[:, 0]), 0), np.around(np.mean(segment[:, 1]), 0)
|
|
|
+ cx, cy = int(cx), int(cy)
|
|
|
+ sheet1.merge_range(start + j, 4, start + j, 5, "(" + str(cx) + "," + str(cy) + ")",
|
|
|
+ soft) # neirong[j]['points']
|
|
|
+ sheet1.merge_range(start + j, 6, start + j, 7, str(area), soft)
|
|
|
+ sheet1.merge_range(start + j, 8, start + j, 9, str(lenth), soft)
|
|
|
+ sheet1.merge_range(start + j, 10, start + j, 11, str(width), soft)
|
|
|
+
|
|
|
+ start = last + 1
|
|
|
+ # print("after img_list: ", len(img_list), img_list)
|
|
|
+ writebook.close()
|
|
|
+
|
|
|
+ for img_f in glob.glob(src_path + "/*_forxlsx.png"):
|
|
|
+ os.remove(img_f)
|
|
|
+ # print("remove success: ", img_f)
|
|
|
+
|
|
|
+
|
|
|
+class WriteThread(QThread):
|
|
|
+ # write_xlsxwriter(msg,self.img_list,file_path)
|
|
|
+ send_msg = pyqtSignal(str) # 信号
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ super(WriteThread, self).__init__()
|
|
|
+
|
|
|
+ self.msg = ""
|
|
|
+ self.imgs = []
|
|
|
+ self.path = ""
|
|
|
+ self.src = ""
|
|
|
+ self.gsd = 0
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ try:
|
|
|
+ self.send_msg.emit("开始导出报表")
|
|
|
+ write_xlsxwriter_new(self.msg, self.imgs, self.path, self.src, self.gsd)
|
|
|
+ self.send_msg.emit("导出报表完成")
|
|
|
+ except Exception as e:
|
|
|
+ print('WriteThread %s' % e)
|
|
|
+ self.send_msg.emit("error")
|
|
|
+
|
|
|
+
|
|
|
+# 消息框 显示 进度
|
|
|
+class Window(QMainWindow, Ui_mainWindow):
|
|
|
+ def __init__(self):
|
|
|
+ super(Window, self).__init__()
|
|
|
+
|
|
|
+ self.wel = Welcome()
|
|
|
+ self.wel.login.show()
|
|
|
+ self.wel.success.connect(self.new_init_)
|
|
|
+ self.gsd_file = ""
|
|
|
+
|
|
|
+ def new_init_(self):
|
|
|
+ if self.wel.success:
|
|
|
+ self.setupUi(self)
|
|
|
+ ## 加载子窗口
|
|
|
+ self.ui_detect = loadUi("qt_win/detect_par.ui") # 检测的参数选择
|
|
|
+ self.ui_train = loadUi("qt_win/train_par.ui") # 训练时的参数选择
|
|
|
+
|
|
|
+ # 训练子窗口 功能连接函数
|
|
|
+ self.ui_train.pushButton_2.clicked.connect(self.SetTrainOK)
|
|
|
+ self.ui_train.pushButton.clicked.connect(self.restore_set)
|
|
|
+ self.ui_train.pushButton_3.clicked.connect(self.choose_train_file)
|
|
|
+
|
|
|
+ self.ui_detect.pushButton_2.clicked.connect(self.SetDetectOK)
|
|
|
+ self.ui_detect.pushButton.clicked.connect(self.restore_set)
|
|
|
+
|
|
|
+ # flag
|
|
|
+ self.m_flag = False # 鼠标事件标志
|
|
|
+ self.t_flag = 0 # 训练标志,为False,所以在new_init_函数调用时默认模式是检测
|
|
|
+ self.DC = "0"
|
|
|
+ self.count_T = 0
|
|
|
+ self.count_D = 0 # 当零的时候 即使用已存在预测结果画图
|
|
|
+ # self.count_T_error = 0
|
|
|
+ # self.count_D_error = 0
|
|
|
+ # win10的CustomizeWindowHint模式,边框上面有一段空白
|
|
|
+ self.setWindowFlags(Qt.FramelessWindowHint) # 去掉操作系统标题栏
|
|
|
+
|
|
|
+ # 自定义标题栏按钮
|
|
|
+ self.minButton.clicked.connect(self.showMinimized)
|
|
|
+ self.maxButton.clicked.connect(self.max_or_restore)
|
|
|
+ self.closeButton.clicked.connect(self.close)
|
|
|
+ # 加拖动的功能
|
|
|
+
|
|
|
+ # 定时清空自定义状态栏上的文字
|
|
|
+ self.qtimer = QTimer(self)
|
|
|
+ self.qtimer.setSingleShot(True)
|
|
|
+ self.qtimer.timeout.connect(lambda: self.statistic_label.clear())
|
|
|
+
|
|
|
+ # 训练和推理 功能选择按钮
|
|
|
+ cb_group = QButtonGroup()
|
|
|
+ cb_group.addButton(self.trainbutton, 0)
|
|
|
+ cb_group.addButton(self.detectbutton, 1)
|
|
|
+ cb_group.addButton(self.trainbutton_2, 2) # 新增功能
|
|
|
+ cb_group.addButton(self.trainbutton_3, 3)
|
|
|
+ cb_group.addButton(self.trainbutton_4, 4)
|
|
|
+ cb_group.addButton(self.trainbutton_5, 5)
|
|
|
+ cb_group.setExclusive(True) # 设置button互斥
|
|
|
+
|
|
|
+ self.trainbutton.clicked.connect(self.chose_training)
|
|
|
+ self.trainbutton_2.clicked.connect(self.chose_feature) # 新增功能
|
|
|
+ self.trainbutton_3.clicked.connect(self.chose_jiance)
|
|
|
+ self.trainbutton_4.clicked.connect(self.chose_q_jaince)
|
|
|
+ self.trainbutton_5.clicked.connect(self.chose_gnn)
|
|
|
+ self.detectbutton.clicked.connect(self.chose_detecting) # 主页面检测按钮的操函数,用于弹出检测参数设置对话框
|
|
|
+
|
|
|
+
|
|
|
+ # 增加功能
|
|
|
+ # 自动搜索模型 -- 增加 新训练 保存的模型
|
|
|
+ self.model_path = './weights/' # 指定 保存模型路径
|
|
|
+ self.feature_alg = './test_data/xie' # 新增功能
|
|
|
+ self.comboBox.clear()
|
|
|
+ pt_lists = os.listdir(self.model_path)
|
|
|
+ py_lists = os.listdir(self.feature_alg) # 新增功能
|
|
|
+ if self.t_flag == 1: # 特征提取模式
|
|
|
+ py_lists = [file for file in py_lists if file.endswith('.py') and "feature" in file] # 新增功能
|
|
|
+ if self.t_flag == 0: # 识别模式
|
|
|
+ pt_lists = [file for file in pt_lists if file.endswith('.pt') and "yolov5" not in file]
|
|
|
+ if self.t_flag == 2: # 训练模式
|
|
|
+ pt_lists = [file for file in pt_lists if file.endswith('.pt') and "yolov5" in file]
|
|
|
+ if self.t_flag == 3 or self.t_flag == 4: # 检测
|
|
|
+ py_lists = [file for file in py_lists if file.endswith('.py') and "detection" in file]
|
|
|
+ if self.t_flag == 4:
|
|
|
+ pass
|
|
|
+ if self.t_flag == 5:
|
|
|
+ pass
|
|
|
+
|
|
|
+ py_lists.sort(reverse=True) # 新增功能
|
|
|
+ pt_lists.sort(reverse=True) # 为True是降序
|
|
|
+
|
|
|
+
|
|
|
+ self.current_img = ''
|
|
|
+ self.pt_lists = pt_lists
|
|
|
+ self.py_lists = py_lists # 新增功能
|
|
|
+
|
|
|
+ self.comboBox.addItems(self.pt_lists) # 列表中的每一项作为一个独立的选项加入到 comboBox 控件中。这使得用户可以从下拉菜单中选择一个项目。
|
|
|
+ self.comboBox.addItems(self.py_lists) # 新增功能
|
|
|
+
|
|
|
+ self.comboBox.currentTextChanged.connect(self.change_model) # 在comboBox 下拉菜单中选择一个不同的项目时,程序会自动调用 self.change_model 方法。
|
|
|
+
|
|
|
+ # 自动搜索超参数文件
|
|
|
+ self.hyps_path = './data/hyps/' # 指定 超参数路径
|
|
|
+ self.ui_train.comboBox.clear()
|
|
|
+ self.hyp_list = os.listdir(self.hyps_path)
|
|
|
+ # print("self.hpy_list: ",self.hyp_list)
|
|
|
+ self.hyp_list = [file for file in self.hyp_list if file.endswith('-low.yaml')]
|
|
|
+ # print("after self.hpy_list: ",self.hyp_list)
|
|
|
+ self.hyp_list.sort(key=lambda x: os.path.getsize(self.hyps_path + x))
|
|
|
+ self.ui_train.comboBox.addItems(self.hyp_list)
|
|
|
+ self.restore_set()
|
|
|
+
|
|
|
+ self.Base_Path_D = "./runs/detect/base"
|
|
|
+ Path(self.Base_Path_D).mkdir(parents=True, exist_ok=True) # 缓存json
|
|
|
+ dtime = (QDateTime.currentDateTime().toString(Qt.ISODate)).split("T")[0]
|
|
|
+ self.time = QDateTime.currentDateTime().toString(Qt.ISODate)
|
|
|
+ self.runs_path = f"./runs/{self.wel.account}/{dtime}/detect" # 保存推理的路径 可以结合软件运行的时间
|
|
|
+
|
|
|
+ if os.path.exists(self.runs_path):
|
|
|
+ shutil.rmtree(self.runs_path) # os.rmdir(self.runs_path)
|
|
|
+ if os.path.exists(self.runs_path.replace('detect', 'train')):
|
|
|
+ shutil.rmtree(self.runs_path.replace('detect', 'train')) # os.rmdir(self.runs_path)
|
|
|
+ # self.runs_path = "./runs/detect" #保存推理的路径 可以结合软件运行的时间
|
|
|
+
|
|
|
+ # 系统信息框 -- 返回训练或者推理的过程信息 以及最终结果的统计信息等
|
|
|
+ # 后续包装返回信息 #实例化列表模型,添加数据
|
|
|
+ # self.listView.addItem(self.time + "\t软件执行信息") # slm = QStringListModel()
|
|
|
+ # self.listView.itemDoubleClicked.connect(self.listViewdoubleClicked)
|
|
|
+ # self.listView.itemClicked.connect(self.listViewClicked)
|
|
|
+ # 读取 文件夹或文件中的图片
|
|
|
+ self.img_list = [] # [r'images\one.jpg',r'images\two.jpg',r'images\three.jpg',r'images\four.jpg'] #存放图片文件名 -- 绝对路径
|
|
|
+ self.output_file_list = {}
|
|
|
+ self.index = 0
|
|
|
+ self.choose_file.clicked.connect(self.open_file)
|
|
|
+ self.choose_folder.clicked.connect(self.open_folder)
|
|
|
+ self.resultWidget.itemClicked.connect(self.WidgetClicked)
|
|
|
+
|
|
|
+ # # 选择圆盘
|
|
|
+ # self.choose_circle.clicked.connect(self.get_choose_circle)
|
|
|
+ # self.gsd.textChanged.connect(lambda current_text: self.gsd_textChanged_func(current_text)) # 槽函数
|
|
|
+
|
|
|
+ # 显示图片
|
|
|
+ self.pre_page.clicked.connect(self.change_image_up) # 上一页
|
|
|
+ self.next_page.clicked.connect(self.change_image_down) # 下一页
|
|
|
+ self.show_result.clicked.connect(self.show_output) # 显示结果
|
|
|
+ # self.show_result.clicked.connect(self.show_res) # 显示结果
|
|
|
+
|
|
|
+ ### 结果导出##
|
|
|
+ self.result_export.clicked.connect(self.save_output) # save
|
|
|
+
|
|
|
+ # 1.Qt 开一个 yolov5 线程 #用于模型推理
|
|
|
+ self.det_thread = DetThread() # 增加一些槽 来获取返回信息
|
|
|
+ self.det_thread.send_msg.connect(lambda x: self.show_msg(x))
|
|
|
+ # 修正命令行内容
|
|
|
+ # 1.Qt 开一个 yolov5 线程 #用于模型训练
|
|
|
+ self.train_thread = TrainThread()
|
|
|
+ self.train_thread.send_msg.connect(lambda x: self.show_msg2(x))
|
|
|
+
|
|
|
+ self.fea_thread = FeaThread()
|
|
|
+ self.fea_thread.send_msg.connect(lambda x: self.show_msg3(x))
|
|
|
+
|
|
|
+ self.jian_thread = JianThread()
|
|
|
+ self.jian_thread.send_msg.connect(lambda x: self.show_msg2(x))
|
|
|
+ # 2. os.system 后台跑结果出来
|
|
|
+ # 2. os.system 后台跑结果出来
|
|
|
+ self.action.clicked.connect(self.begin_action) # 开始推理
|
|
|
+ self.plots_thread = PlotThread() # 画图子进程
|
|
|
+ self.plots_thread.send_msg.connect(lambda x: self.show_output_new(x)) # 将信号与槽函数连接起来
|
|
|
+ self.plots_flag = False
|
|
|
+ # self.close_info.clicked.connect(self.change_flag)
|
|
|
+
|
|
|
+ self.write_thread = WriteThread() # 写excel子进程
|
|
|
+ self.write_thread.send_msg.connect(lambda x: self.write_xlsxwriter(x))
|
|
|
+
|
|
|
+ ###### TODO: 纠错 ####
|
|
|
+ self.labelme_thread = LabelThread()
|
|
|
+ self.error_correction.clicked.connect(self.open_labelme) # 纠错
|
|
|
+ self.SetDetectOK()
|
|
|
+ self.show()
|
|
|
+
|
|
|
+
|
|
|
+ def gsd_textChanged_func(self, current_text):
|
|
|
+ print("文本框内容变化信号", current_text)
|
|
|
+ # self.textBrowser.append("文本框内容变化信号" + current_text + '\n')
|
|
|
+ lines = []
|
|
|
+
|
|
|
+ if self.shebei.text() != "" and self.beicepin.text() != "" and self.pici.text() != "" and current_text != "":
|
|
|
+ lines.append(" ".join([self.shebei.text(), self.beicepin.text(), self.pici.text(), current_text]))
|
|
|
+ print("lines: ", lines)
|
|
|
+ with open(self.gsd_file, "w", encoding="utf-8") as f:
|
|
|
+ for line in lines:
|
|
|
+ f.writelines(line + "\n")
|
|
|
+ self.out_video.gsd = float(current_text) # 画图类的gsd属
|
|
|
+ self.gsd.setText(str(float(current_text)))
|
|
|
+
|
|
|
+ # 选择圆盘
|
|
|
+
|
|
|
+ def get_choose_circle(self):
|
|
|
+ dis = math.sqrt(math.pow(self.raw_video.circle_p1.x() - self.raw_video.circle_p2.x(), 2) + math.pow(
|
|
|
+ self.raw_video.circle_p1.y() - self.raw_video.circle_p2.y(), 2))
|
|
|
+ print("dis: ", dis)
|
|
|
+ gsd = 0
|
|
|
+ if self.zhijing.text() != "" and dis > 0:
|
|
|
+ print("self.zhijing.text()")
|
|
|
+ gsd = round(int(self.zhijing.text()) / dis, 2)
|
|
|
+ print("gsd: ", gsd)
|
|
|
+ print("self.gsd.setText before :", gsd)
|
|
|
+ self.gsd.setText(str(gsd))
|
|
|
+ print("self.gsd.setText after :", gsd)
|
|
|
+
|
|
|
+ ############后台信息显示 -- 方便调试##################
|
|
|
+ def statistic_msg(self, msg, time=-1):
|
|
|
+ self.statistic_label.setText(msg)
|
|
|
+ self.qtimer.start(time) # 3秒后自动清除
|
|
|
+
|
|
|
+ ######################子窗口部分###############################
|
|
|
+ # 恢复默认参数
|
|
|
+ def restore_set(self):
|
|
|
+ # if not self.trainbutton.isEnabled():
|
|
|
+ self.statistic_msg("恢复默认参数", -1) # 恢复默认 可以不用管 直接把
|
|
|
+ self.ui_train.radioButton_2.setChecked(True) #
|
|
|
+ self.ui_train.radioButton_2.setEnabled(False) # 预训练
|
|
|
+
|
|
|
+ self.ui_train.lineEdit_2.setText("200") # epochs
|
|
|
+ self.ui_train.lineEdit_3.setText("32") # batch size
|
|
|
+ self.ui_train.lineEdit_4.setText("1e-2") # lr
|
|
|
+ self.ui_train.lineEdit_5.setText("640") # img size
|
|
|
+
|
|
|
+ # 设置用户训练的模型的名称
|
|
|
+ # {self.wel.account} / {dtime} / detect
|
|
|
+ # dtime = (QDateTime.currentDateTime().toString(Qt.ISODate)).split("T")[0]
|
|
|
+ # self.ui_train.lineEdit_6.setText(f"{dtime}_{self.wel.account}" + '.pt') # ckpt name
|
|
|
+ current_datetime = datetime.now()
|
|
|
+
|
|
|
+ # 格式化日期和时间,例如:"2024-06-11 15:30:45"
|
|
|
+ formatted_datetime = current_datetime.strftime(f"%Y-%m-%d_%H%M" + ".pt")
|
|
|
+ self.ui_train.lineEdit_6.setText(formatted_datetime) # ckpt name
|
|
|
+
|
|
|
+ self.ui_train.radioButton_3.setChecked(False) # 是否使用图像列表数据加入样本进行再训练
|
|
|
+ self.ui_train.radioButton_3.setEnabled(False) # 设置单选按钮为不可选状态
|
|
|
+
|
|
|
+ self.radio_layout1 = QButtonGroup(self)
|
|
|
+ self.radio_layout2 = QButtonGroup(self)
|
|
|
+ self.radio_layout1.addButton(self.ui_train.radioButton_2)
|
|
|
+ self.radio_layout2.addButton(self.ui_train.radioButton_3)
|
|
|
+
|
|
|
+ # else:
|
|
|
+ # self.statistic_msg("恢复默认检测参数") #恢复默认 可以不用管 直接把
|
|
|
+ self.ui_detect.lineEdit.setText("0.5")
|
|
|
+ self.ui_detect.lineEdit_2.setText("0.4")
|
|
|
+ self.ui_detect.lineEdit_3.setText("256")
|
|
|
+ self.ui_detect.radioButton.setChecked(True) # 是否使用图像切片预测
|
|
|
+ self.ui_detect.radioButton.setEnabled(False) # 设置单选按钮为不可选状态,即只支持切片预测
|
|
|
+ self.ui_detect.lineEdit_4.setText("640") # 判断元组还数字
|
|
|
+
|
|
|
+ ## 子窗口弹出
|
|
|
+ def chose_training(self):
|
|
|
+ self.statistic_msg("选择训练功能,请设置超参数", -1)
|
|
|
+ self.ui_detect.close()
|
|
|
+ self.ui_train.show()
|
|
|
+ self.t_flag = 2
|
|
|
+ self.search_pt()
|
|
|
+ if self.trainbutton.isEnabled(): # 增加flag
|
|
|
+ self.trainbutton.setEnabled(False)
|
|
|
+ self.trainbutton_4.setEnabled(True)
|
|
|
+ self.trainbutton_5.setEnabled(True)
|
|
|
+ self.detectbutton.setEnabled(True)
|
|
|
+ self.trainbutton_2.setEnabled(True)
|
|
|
+ self.trainbutton_3.setEnabled(True)
|
|
|
+ else:
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ self.trainbutton_4.setEnabled(False)
|
|
|
+ self.trainbutton_5.setEnabled(False)
|
|
|
+ self.detectbutton.setEnabled(False)
|
|
|
+ self.trainbutton_2.setEnabled(False)
|
|
|
+ self.trainbutton_3.setEnabled(False)
|
|
|
+
|
|
|
+
|
|
|
+ def chose_detecting(self):
|
|
|
+ self.statistic_msg("选择推理功能,请设置超参数", -1)
|
|
|
+ self.t_flag = 0
|
|
|
+ self.search_pt()
|
|
|
+ self.ui_detect.show()
|
|
|
+ self.ui_train.close()
|
|
|
+ if self.detectbutton.isEnabled(): # 增加flag
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ self.trainbutton_4.setEnabled(True)
|
|
|
+ self.trainbutton_5.setEnabled(True)
|
|
|
+ self.detectbutton.setEnabled(False)
|
|
|
+ self.trainbutton_2.setEnabled(True)
|
|
|
+ self.trainbutton_3.setEnabled(True)
|
|
|
+ else:
|
|
|
+ self.detectbutton.setEnabled(True)
|
|
|
+ self.trainbutton_4.setEnabled(False)
|
|
|
+ self.trainbutton_5.setEnabled(False)
|
|
|
+ self.trainbutton.setEnabled(False)
|
|
|
+ self.trainbutton_2.setEnabled(False)
|
|
|
+ self.trainbutton_3.setEnabled(False)
|
|
|
+
|
|
|
+ def chose_feature(self):
|
|
|
+ self.t_flag = 1
|
|
|
+ self.search_py()
|
|
|
+ self.ui_detect.close()
|
|
|
+ self.ui_train.close()
|
|
|
+ if self.trainbutton_2.isEnabled(): # 增加flag
|
|
|
+ self.trainbutton_2.setEnabled(False)
|
|
|
+ self.trainbutton_4.setEnabled(True)
|
|
|
+ self.trainbutton_5.setEnabled(True)
|
|
|
+ self.detectbutton.setEnabled(True)
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ self.trainbutton_3.setEnabled(True)
|
|
|
+ else:
|
|
|
+ self.trainbutton_2.setEnabled(True)
|
|
|
+ self.trainbutton_4.setEnabled(False)
|
|
|
+ self.trainbutton_5.setEnabled(False)
|
|
|
+ self.detectbutton.setEnabled(False)
|
|
|
+ self.trainbutton.setEnabled(False)
|
|
|
+ self.trainbutton_3.setEnabled(False)
|
|
|
+
|
|
|
+
|
|
|
+ def chose_jiance(self):
|
|
|
+ self.t_flag = 3
|
|
|
+ self.search_py()
|
|
|
+ self.ui_detect.close()
|
|
|
+ self.ui_train.close()
|
|
|
+ if self.trainbutton_3.isEnabled(): # 增加flag
|
|
|
+ self.trainbutton_3.setEnabled(False)
|
|
|
+ self.trainbutton_4.setEnabled(True)
|
|
|
+ self.trainbutton_5.setEnabled(True)
|
|
|
+ self.trainbutton_2.setEnabled(True)
|
|
|
+ self.detectbutton.setEnabled(True)
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ else:
|
|
|
+ self.trainbutton_3.setEnabled(True)
|
|
|
+ self.trainbutton_4.setEnabled(False)
|
|
|
+ self.trainbutton_5.setEnabled(False)
|
|
|
+ self.trainbutton_2.setEnabled(False)
|
|
|
+ self.detectbutton.setEnabled(False)
|
|
|
+ self.trainbutton.setEnabled(False)
|
|
|
+
|
|
|
+ def chose_q_jaince(self):
|
|
|
+ self.t_flag = 4
|
|
|
+ self.search_py()
|
|
|
+ self.ui_detect.close()
|
|
|
+ self.ui_train.close()
|
|
|
+ if self.trainbutton_4.isEnabled(): # 增加flag
|
|
|
+ self.trainbutton_4.setEnabled(False)
|
|
|
+ self.trainbutton_5.setEnabled(True)
|
|
|
+ self.trainbutton_3.setEnabled(True)
|
|
|
+ self.trainbutton_2.setEnabled(True)
|
|
|
+ self.detectbutton.setEnabled(True)
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ else:
|
|
|
+ self.trainbutton_4.setEnabled(True)
|
|
|
+ self.trainbutton_5.setEnabled(False)
|
|
|
+ self.trainbutton_3.setEnabled(False)
|
|
|
+ self.trainbutton_2.setEnabled(False)
|
|
|
+ self.detectbutton.setEnabled(False)
|
|
|
+ self.trainbutton.setEnabled(False)
|
|
|
+
|
|
|
+ def chose_gnn(self):
|
|
|
+ self.t_flag = 5
|
|
|
+ self.search_py()
|
|
|
+ self.ui_detect.close()
|
|
|
+ self.ui_train.close()
|
|
|
+ if self.trainbutton_5.isEnabled(): # 增加flag
|
|
|
+ self.trainbutton_4.setEnabled(True)
|
|
|
+ self.trainbutton_5.setEnabled(False)
|
|
|
+ self.trainbutton_3.setEnabled(True)
|
|
|
+ self.trainbutton_2.setEnabled(True)
|
|
|
+ self.detectbutton.setEnabled(True)
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ else:
|
|
|
+ self.trainbutton_4.setEnabled(False)
|
|
|
+ self.trainbutton_5.setEnabled(True)
|
|
|
+ self.trainbutton_3.setEnabled(False)
|
|
|
+ self.trainbutton_2.setEnabled(False)
|
|
|
+ self.detectbutton.setEnabled(False)
|
|
|
+ self.trainbutton.setEnabled(False)
|
|
|
+
|
|
|
+
|
|
|
+ def choose_train_file(self):
|
|
|
+ print("choose_file")
|
|
|
+ # 多选对话框
|
|
|
+ file_dialog = QFileDialog()
|
|
|
+ file_dialog.setFileMode(QFileDialog.DirectoryOnly)
|
|
|
+ file_dialog.setOption(QFileDialog.DontUseNativeDialog, True)
|
|
|
+ file_dialog.setDirectory('d:/data')
|
|
|
+ file_view = file_dialog.findChild(QListView, 'listView')
|
|
|
+ if file_view:
|
|
|
+ file_view.setSelectionMode(QAbstractItemView.MultiSelection)
|
|
|
+ f_tree_view = file_dialog.findChild(QTreeView)
|
|
|
+ if f_tree_view:
|
|
|
+ f_tree_view.setSelectionMode(QAbstractItemView.MultiSelection)
|
|
|
+ if file_dialog.exec_():
|
|
|
+ folder = file_dialog.selectedFiles()
|
|
|
+ print("folder: ", folder)
|
|
|
+ line = ""
|
|
|
+ for f in folder:
|
|
|
+ line += f + ";"
|
|
|
+ self.ui_train.lineEdit_7.setText(line)
|
|
|
+
|
|
|
+ def SetTrainOK(self):
|
|
|
+ self.statistic_msg("训练参数设置完毕", -1)
|
|
|
+ # 打开训练设置窗口 获取参数
|
|
|
+ # list 存放 #str 类型
|
|
|
+ self.train_thread.Command = [str(self.ui_train.radioButton_2.isChecked()), self.ui_train.lineEdit_5.text(),
|
|
|
+ self.ui_train.lineEdit_2.text(), \
|
|
|
+ self.ui_train.lineEdit_3.text(), self.ui_train.lineEdit_4.text(), \
|
|
|
+ self.model_path + self.ui_train.lineEdit_6.text(),
|
|
|
+ self.hyps_path + self.ui_train.comboBox.currentText(),
|
|
|
+ self.runs_path.replace('detect', 'train'), \
|
|
|
+ self.ui_train.lineEdit_7.text()]
|
|
|
+ if self.ui_train.radioButton_2.isChecked():
|
|
|
+ self.traing_hyp = f" 启动{self.comboBox.currentText()}预训练 "
|
|
|
+ else:
|
|
|
+ self.traing_hyp = " 不启动预训练 "
|
|
|
+ self.traing_hyp += f"img size:{self.ui_train.lineEdit_5.text()} " + f"epochs:{self.ui_train.lineEdit_5.text()} bs:{self.ui_train.lineEdit_3.text()} " + \
|
|
|
+ f"lr:{self.ui_train.lineEdit_4.text()} " + f"hyp:{self.ui_train.comboBox.currentText()}"
|
|
|
+ print("self.train_thread.Command,self.traing_hyp:", self.train_thread.Command, self.traing_hyp)
|
|
|
+ # 关闭窗口
|
|
|
+ self.ui_train.close()
|
|
|
+
|
|
|
+ def SetDetectOK(self):
|
|
|
+ self.statistic_msg("检测参数设置完毕", -1)
|
|
|
+
|
|
|
+ # 打开检测设置窗口 获取参数
|
|
|
+ # list 存放 #str 类型
|
|
|
+ self.det_thread.Command = [self.ui_detect.lineEdit_4.text(), self.ui_detect.lineEdit.text(),
|
|
|
+ self.ui_detect.lineEdit_2.text(), self.ui_detect.lineEdit_3.text(), self.runs_path]
|
|
|
+
|
|
|
+ if self.ui_detect.radioButton.isChecked():
|
|
|
+ self.det_thread.split = True
|
|
|
+ self.detect_hyp = f" 启动重叠{self.ui_detect.lineEdit_3.text()}的切片预测 "
|
|
|
+ else:
|
|
|
+ self.det_thread.split = False
|
|
|
+ self.detect_hyp = " 不启动切片预测 "
|
|
|
+ self.detect_hyp += f"img size:{self.ui_detect.lineEdit_4.text()} " + f"IOU:{self.ui_detect.lineEdit.text()} 置信度:{self.ui_detect.lineEdit_2.text()}"
|
|
|
+ # 关闭窗口
|
|
|
+ if self.ui_detect.isVisible() == True: # 我加的,如果窗口没有打开,就不用关闭了
|
|
|
+ self.ui_detect.close()
|
|
|
+ # print("self.det_thread.Command,split", self.det_thread.Command, self.det_thread.split)
|
|
|
+
|
|
|
+ ###########设置区---- 模型选择部分#############
|
|
|
+ # 模型选择
|
|
|
+ def change_model(self, x):
|
|
|
+ self.model_type = self.comboBox.currentText()
|
|
|
+ self.statistic_msg('模型切换为%s' % x, -1)
|
|
|
+
|
|
|
+ # 规定存放模型路径 -- update 模型名字
|
|
|
+ def search_pt(self):
|
|
|
+ pt_lists = os.listdir(self.model_path) #
|
|
|
+ print("search_pt pt_lists: ", pt_lists)
|
|
|
+ if self.t_flag == 0: # 检测模式
|
|
|
+ pt_lists = [file for file in pt_lists if file.endswith('.pt') and "yolov5" not in file]
|
|
|
+ elif self.t_flag == 2:
|
|
|
+ pt_lists = [file for file in pt_lists if file.endswith('.pt') and "yolov5" in file]
|
|
|
+
|
|
|
+ print("after search_pt pt_lists: ", self.t_flag, pt_lists)
|
|
|
+
|
|
|
+ pt_lists.sort(reverse=True)
|
|
|
+ # if pt_lists != self.pt_lists:
|
|
|
+ self.pt_lists = pt_lists
|
|
|
+ self.comboBox.clear()
|
|
|
+ self.comboBox.addItems(self.pt_lists)
|
|
|
+
|
|
|
+ def search_py(self):
|
|
|
+ py_lists = os.listdir(self.feature_alg) #
|
|
|
+ print("search_py py_lists: ", py_lists)
|
|
|
+ if self.t_flag == 1: # 特征提取模式
|
|
|
+ py_lists = [file for file in py_lists if file.endswith('.py') and "feature" in file]
|
|
|
+ elif self.t_flag == 3 or self.t_flag == 4: # 检测模式
|
|
|
+ py_lists = [file for file in py_lists if file.endswith('.py') and 'detection' in file]
|
|
|
+ print("after search_pt pt_lists: ", self.t_flag, py_lists)
|
|
|
+
|
|
|
+ py_lists.sort(reverse=True)
|
|
|
+ # if pt_lists != self.pt_lists:
|
|
|
+ self.py_lists = py_lists
|
|
|
+ self.comboBox.clear()
|
|
|
+ self.comboBox.addItems(self.py_lists)
|
|
|
+
|
|
|
+ def show_msg(self, msg):
|
|
|
+ if "error" in msg.lower():
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ MessageBox(self.closeButton, title='提示', text='检测过程出现错误,请查看相关日志文件', time=1000,
|
|
|
+ auto=True).exec_()
|
|
|
+ # self.count_D_error += 1
|
|
|
+ elif msg == "开始检测":
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ self.tips = MessageBox(
|
|
|
+ self.closeButton, title='提示', text=f'请稍等,正在进行第{self.count_D}次检测', time=500,
|
|
|
+ auto=False) # .exec_()
|
|
|
+ self.tips.show()
|
|
|
+
|
|
|
+ elif msg == "检测结束":
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ self.tips.close()
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示', text=f'第{self.count_D}次检测结束,正在关闭提示窗口', time=1000,
|
|
|
+ auto=True).exec_()
|
|
|
+ # self.tips.show()
|
|
|
+ # self.listView.addItem(QDateTime.currentDateTime().toString(Qt.ISODate) + f"\t第{self.count_D}次检测结束,超参数:" + self.detect_hyp) #双击查看日志
|
|
|
+ # self.statistic_label.clear()
|
|
|
+
|
|
|
+ def show_msg2(self, msg):
|
|
|
+ if "error" in msg.lower():
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示', text='训练过程出现错误,请查看相关日志文件', time=1000,
|
|
|
+ auto=True).exec_()
|
|
|
+ # 这里 因为训练错误 来一个计算失败的
|
|
|
+ # count_T_error += 1
|
|
|
+ elif msg == "开始训练":
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ MessageBox(self.closeButton, title='提示', text=f'请稍等,正在进行第{self.count_T}次训练', time=5000,
|
|
|
+ auto=True).exec_()
|
|
|
+ # QMessageBox.information(self,"提示", f'请稍等,正在进行第{self.count_T}次训练。。。')
|
|
|
+ # InfoMessageBox.information(self, "提示", f'请稍等,正在进行第{self.count_T}次训练。。。')
|
|
|
+ # self.tips.show()
|
|
|
+ self.statistic_msg("正在训练中,请不要操作", -1)
|
|
|
+ elif msg == "训练结束":
|
|
|
+ # self.train_tips.close()
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示', text=f'第{self.count_T}次训练结束,正在关闭提示窗口。。。', time=500,
|
|
|
+ auto=True).exec_()
|
|
|
+ # self.statistic_label.clear()
|
|
|
+ # self.listView.addItem(QDateTime.currentDateTime().toString(Qt.ISODate) + f"\t第{self.count_T}次训练j结束,超参数:" + self.traing_hyp)
|
|
|
+ # self.search_pt()
|
|
|
+
|
|
|
+ def show_msg3(self, msg):
|
|
|
+ if "error" in msg.lower():
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ MessageBox(self.closeButton, title='提示', text='特征提取过程出现错误,请查看相关日志文件', time=1000,
|
|
|
+ auto=True).exec_()
|
|
|
+ # self.count_D_error += 1
|
|
|
+ elif msg == "开始特征提取":
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ self.tips = MessageBox(
|
|
|
+ self.closeButton, title='提示', text=f'请稍等,正在进行第{self.count_D}次提取。。。', time=500,
|
|
|
+ auto=False) # .exec_()
|
|
|
+ self.tips.show()
|
|
|
+
|
|
|
+ elif msg == "特征提取结束":
|
|
|
+ self.statistic_msg(msg, -1)
|
|
|
+ self.tips.close()
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示', text=f'第{self.count_D}次检测结束,正在关闭提示窗口。。。', time=1000,
|
|
|
+ auto=True).exec_()
|
|
|
+
|
|
|
+ ################# 自定义标题栏按钮--功能##########################
|
|
|
+ def max_or_restore(self):
|
|
|
+ if self.maxButton.isChecked():
|
|
|
+ self.showMaximized()
|
|
|
+ else:
|
|
|
+ self.showNormal()
|
|
|
+
|
|
|
+ def closeEvent(self, event):
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示', text='请稍等,正在关闭程序。。。', time=500, auto=True).exec_()
|
|
|
+ # #删除文件 来kill进程
|
|
|
+ # if self.count_T == 1:
|
|
|
+ # if os.path.exists(os.path.join(self.runs_path.replace('detect','train'),f'exp{self.count_T}')):
|
|
|
+ # shutil.rmtree(os.path.join(self.runs_path.replace('detect','train'),f'exp{self.count_T}')) #os.rmdir(self.runs_path)
|
|
|
+ # else:
|
|
|
+ # if os.path.exists(os.path.join(self.runs_path.replace('detect','train'),f'exp')):
|
|
|
+ # shutil.rmtree(os.path.join(self.runs_path.replace('detect','train'),f'exp')) #os.rmdir(self.runs_path)
|
|
|
+ self.train_thread.exit() # 没用
|
|
|
+ self.det_thread.exit()
|
|
|
+ # self.train_thread.exit()
|
|
|
+ kill_name("python.exe")
|
|
|
+ sys.exit(0)
|
|
|
+
|
|
|
+ ## 鼠标获取
|
|
|
+ def mousePressEvent(self, event):
|
|
|
+ self.m_Position = event.pos()
|
|
|
+ if event.button() == Qt.LeftButton:
|
|
|
+ if 0 < self.m_Position.x() < self.groupBox.pos().x() + self.groupBox.width() and \
|
|
|
+ 0 < self.m_Position.y() < self.groupBox.pos().y() + self.groupBox.height():
|
|
|
+ self.m_flag = True
|
|
|
+
|
|
|
+ def mouseMoveEvent(self, QMouseEvent):
|
|
|
+ if Qt.LeftButton and self.m_flag:
|
|
|
+ self.move(QMouseEvent.globalPos() - self.m_Position) # 更改窗口位置
|
|
|
+ # QMouseEvent.accept()
|
|
|
+
|
|
|
+ def mouseReleaseEvent(self, QMouseEvent):
|
|
|
+ self.m_flag = False
|
|
|
+ # self.setCursor(QCursor(Qt.ArrowCursor))
|
|
|
+
|
|
|
+ ######################可视化图片####################
|
|
|
+ # 上一页
|
|
|
+ def change_image_up(self):
|
|
|
+ try:
|
|
|
+ self.index = self.index - 1
|
|
|
+ if self.index <= 0:
|
|
|
+ self.index = 0
|
|
|
+ if len(self.img_list) > 0:
|
|
|
+ img_file = self.img_list[self.index] # 图像列表
|
|
|
+ self.current_img = img_file
|
|
|
+ # img = cv2.imread(img_file)
|
|
|
+ self.statistic_msg(img_file, -1) #
|
|
|
+ self.show_image(img_file, self.raw_video)
|
|
|
+ self.label_6.setText(f"{os.path.basename(img_file)}原图像")
|
|
|
+
|
|
|
+ currRow = self.resultWidget.currentRow()
|
|
|
+ if currRow > 0:
|
|
|
+ self.resultWidget.setCurrentRow(currRow - 1)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e), -1)
|
|
|
+
|
|
|
+ # 下一页
|
|
|
+
|
|
|
+ def change_image_down(self):
|
|
|
+ try:
|
|
|
+ self.index = self.index + 1 #
|
|
|
+ if len(self.img_list) > 0:
|
|
|
+ if self.index >= len(self.img_list) - 1:
|
|
|
+ self.index = len(self.img_list) - 1
|
|
|
+ img_file = self.img_list[self.index] # 图像列表
|
|
|
+ self.current_img = img_file
|
|
|
+ self.statistic_msg(img_file) #
|
|
|
+ self.show_image(img_file, self.raw_video)
|
|
|
+ self.label_6.setText(f"{os.path.basename(img_file)}原图像")
|
|
|
+
|
|
|
+ currRow = self.resultWidget.currentRow()
|
|
|
+ rowAll = self.resultWidget.count()
|
|
|
+ if currRow < rowAll - 1:
|
|
|
+ self.resultWidget.setCurrentRow(currRow + 1)
|
|
|
+
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+ # def change_flag(self):
|
|
|
+ # 显示结果
|
|
|
+ def show_res(self):
|
|
|
+ try:
|
|
|
+ if len(self.img_list) > 0:
|
|
|
+ if self.index >= len(self.img_list) - 1:
|
|
|
+ self.index = len(self.img_list) - 1
|
|
|
+ img_file = self.img_list[self.index] # 图像列表
|
|
|
+ self.current_img = img_file
|
|
|
+ self.statistic_msg(img_file) #
|
|
|
+ self.show_image(img_file, self.raw_video)
|
|
|
+ self.label_6.setText(f"{os.path.basename(img_file)}原图像")
|
|
|
+
|
|
|
+ currRow = self.resultWidget.currentRow()
|
|
|
+ rowAll = self.resultWidget.count()
|
|
|
+ if currRow < rowAll - 1:
|
|
|
+ self.resultWidget.setCurrentRow(currRow + 1)
|
|
|
+
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+ # def change_flag(self):
|
|
|
+ # self.plots_flag = not self.plots_flag #取反
|
|
|
+ # if self.plots_flag:#显示信息
|
|
|
+ # self.close_info.setText("关闭信息显示")
|
|
|
+ # else:
|
|
|
+ # self.close_info.setText("打开信息显示")
|
|
|
+ # 输出显示
|
|
|
+ def show_output(self):
|
|
|
+ try:
|
|
|
+ if self.DC != "0":
|
|
|
+ self.out_video.clear()
|
|
|
+ if self.t_flag == 1 or self.t_flag == 3 or self.t_flag == 4:
|
|
|
+ curr_path = self.output_file_list[self.img_list[self.index]]
|
|
|
+ self.plots_thread.img_name = curr_path
|
|
|
+ self.plots_thread.out_path = curr_path
|
|
|
+ self.plots_thread.flag = self.plots_flag
|
|
|
+ self.plots_thread.start() # 开启
|
|
|
+
|
|
|
+ # elif self.t_flag == 0:
|
|
|
+ # curr_path = "D:\\hiddz\\SAR\\runs\\asd\\2024-06-14\\detect\\exp\\HB14932.JPG"
|
|
|
+ # self.plots_thread.img_name = curr_path
|
|
|
+ # self.plots_thread.out_path = curr_path
|
|
|
+ # self.plots_thread.flag = self.plots_flag
|
|
|
+ # self.plots_thread.start()
|
|
|
+
|
|
|
+ else:
|
|
|
+
|
|
|
+ s = f'exp' if self.DC == "1" else f'exp{self.DC}'
|
|
|
+
|
|
|
+ ext = os.path.basename(self.img_list[self.index]).split('.')[-1] # 后缀名
|
|
|
+ # print("show_output: ", os.path.basename(self.img_list[self.index]) + f"第{self.DC}次检测结果") #201312040003B.jpeg第1次检测结果
|
|
|
+ self.statistic_msg(os.path.basename(self.img_list[self.index]) + f"第{self.DC}次检测结果")
|
|
|
+
|
|
|
+ # self.plots_thread.json_file = os.path.join(self.runs_path, s, "jsons",
|
|
|
+ # os.path.basename(self.img_list[self.index]).replace(ext,
|
|
|
+ # 'json'))
|
|
|
+ img_name = self.plots_thread.img_name = self.img_list[self.index].split('\\')[1]
|
|
|
+ path = os.path.join(self.runs_path, s, img_name)
|
|
|
+ # path = "D:\\hiddz\\SAR\\runs\\asd\\2024-06-14\\detect\\exp\\HB14932.JPG"
|
|
|
+ # path =
|
|
|
+ # path = self.plots_thread.out_path
|
|
|
+ self.plots_thread.img_name = path
|
|
|
+ self.plots_thread.out_path = path
|
|
|
+ self.plots_thread.flag = self.plots_flag
|
|
|
+ self.plots_thread.start() # 开启
|
|
|
+
|
|
|
+
|
|
|
+ else:
|
|
|
+ self.statistic_msg("目前没有检测结果,需要先进行检测......") # 我将下面的代码全部注释掉了,即:
|
|
|
+ # # 使用默认的 label
|
|
|
+ # #dirs = os.path.dirname(self.img_list[self.index])
|
|
|
+ # ext = os.path.basename(self.img_list[self.index]).split('.')[-1] #后缀名
|
|
|
+ # if os.path.exists(self.img_list[self.index].replace(ext,'json')):
|
|
|
+ # self.statistic_msg("建议首先选择一个模型进行检测,这里使用默认的检测结果")
|
|
|
+ # self.plots_thread.json_file = self.img_list[self.index].replace(ext,'json') #os.path.join(dirs,name.split(name,'json'))
|
|
|
+ # self.plots_thread.img_name = self.img_list[self.index]
|
|
|
+ # self.plots_thread.out_path = self.Base_Path_D
|
|
|
+ # self.plots_thread.flag = self.plots_flag
|
|
|
+ # self.plots_thread.start() #开启
|
|
|
+ # else:
|
|
|
+ # self.statistic_msg("未存在默认的检测结果,请首先选择一个模型进行检测")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+ print(repr(e))
|
|
|
+
|
|
|
+ def show_output_new(self, msg): # 子进程的消息
|
|
|
+ if msg == "error":
|
|
|
+ self.statistic_msg(msg)
|
|
|
+ self.tips.close()
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示', text='模型未检测到结果,可视化原图', time=1000, auto=True).exec_()
|
|
|
+ self.show_image(self.plots_thread.img_name, self.out_video)
|
|
|
+ name = os.path.basename(self.plots_thread.img_name)
|
|
|
+ if self.DC != "0":
|
|
|
+ self.label_9.setText(f"{name}第{self.DC}次检测结果")
|
|
|
+ else:
|
|
|
+ self.label_9.setText("出错了,目前还没有检测过") # 我改的
|
|
|
+ # self.label_9.setText(f"{os.path.basename(self.img_list[self.index])}默认检测结果")
|
|
|
+
|
|
|
+
|
|
|
+ elif msg == "开始显示结果":
|
|
|
+ self.statistic_msg(msg)
|
|
|
+ self.tips = MessageBox(
|
|
|
+ self.closeButton, title='提示', text=f'请稍等,正在可视化检测结果。。。', time=500, auto=False) # $.exec_()
|
|
|
+ self.tips.show()
|
|
|
+
|
|
|
+ elif msg == "显示结果完成":
|
|
|
+ self.statistic_msg(msg)
|
|
|
+ self.tips.close()
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示', text=f'可视化检测结果结束,正在关闭提示窗口。。。', time=1000,
|
|
|
+ auto=True).exec_()
|
|
|
+ if self.t_flag == 1 or self.t_flag == 3 or self.t_flag == 4:
|
|
|
+ name = os.path.basename(self.plots_thread.img_name)
|
|
|
+ self.show_image(self.plots_thread.out_path, self.out_video)
|
|
|
+ else:
|
|
|
+ name = os.path.basename(self.plots_thread.img_name)
|
|
|
+ # self.show_image(os.path.join(self.plots_thread.out_path, name), self.out_video,
|
|
|
+ # json_file=self.plots_thread.json_file)
|
|
|
+ self.show_image(self.plots_thread.out_path, self.out_video)
|
|
|
+ # global det_img
|
|
|
+ # self.show_image(det_img, self.out_video)
|
|
|
+ if self.DC != "0":
|
|
|
+ self.label_9.setText(f"{name}第{self.DC}次检测结果")
|
|
|
+ else:
|
|
|
+ self.label_9.setText("目前还没有检测过") # 我改的
|
|
|
+ # self.label_9.setText(f"{os.path.basename(self.img_list[self.index])}默认检测结果")
|
|
|
+
|
|
|
+ def show_image(self, img_src, label, json_file=""):
|
|
|
+ try:
|
|
|
+ print("show_image, img_src, label,json_file:", img_src, label, json_file)
|
|
|
+ label.set_image(img_src, json_file)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ print(repr(e))
|
|
|
+
|
|
|
+ ######读取图像#######
|
|
|
+ def open_file(self):
|
|
|
+ cur_work_path = os.path.dirname(__file__)
|
|
|
+ cur_file_path = None
|
|
|
+ if os.path.exists(cur_work_path + "/last_open.txt"):
|
|
|
+ with open(cur_work_path + "/last_open.txt", "r") as dir_f:
|
|
|
+ lines = dir_f.readlines()
|
|
|
+ if len(lines) > 0:
|
|
|
+ cur_file_path = lines[0]
|
|
|
+ # print("cur_work_path cur_file_path: ", cur_work_path, cur_file_path)
|
|
|
+ # folder = QFileDialog.getExistingDirectory(self, '选择图像文件夹...', cur_path)
|
|
|
+
|
|
|
+ source = QFileDialog.getOpenFileName(self, '选取图片', cur_file_path if cur_file_path else cur_work_path,
|
|
|
+ "JPEG Files(*.jpg *.jpeg);;PNG Files(*.png);;BMP Files(*.bmp);;TIF Files(*.tif *.tiff)")
|
|
|
+ # print("source: ", source)
|
|
|
+ if source[0] != "":
|
|
|
+ # 每次更换文件夹 所有检测结果就清除掉
|
|
|
+ if os.path.exists(self.runs_path):
|
|
|
+ shutil.rmtree(self.runs_path) # os.rmdir(self.runs_path)
|
|
|
+ if os.path.exists(self.runs_path.replace('detect', 'train')):
|
|
|
+ shutil.rmtree(self.runs_path.replace('detect', 'train')) # os.rmdir(self.runs_path)
|
|
|
+ # 显示窗口清除
|
|
|
+ # self.listView.clear()
|
|
|
+ # self.listView.addItem(self.time + "\t软件执行信息")
|
|
|
+ # 计算器清零
|
|
|
+ self.count_D = 0
|
|
|
+ self.DC = str(self.count_D)
|
|
|
+ self.count_T = 0
|
|
|
+
|
|
|
+ self.img_list.clear()
|
|
|
+ self.img_list.append(source[0])
|
|
|
+ self.show_list()
|
|
|
+
|
|
|
+ with open(cur_work_path + "/last_open.txt", "w") as dir_f:
|
|
|
+ dir_f.writelines(source[0])
|
|
|
+
|
|
|
+ self.gsd_file = os.path.join(os.path.dirname(source[0]), "gsd.txt")
|
|
|
+ print("open_file gsd_file: ", self.gsd_file)
|
|
|
+ if os.path.exists(self.gsd_file):
|
|
|
+ with open(self.gsd_file, "r", encoding="utf-8") as f:
|
|
|
+ for line in f.readlines():
|
|
|
+ line = line.strip()
|
|
|
+ print("line: ", line)
|
|
|
+ if len(line.split(" ")) == 4:
|
|
|
+ shebei_, beicepin_, pici_, gsd_ = line.split(" ")
|
|
|
+ shebei = shebei_
|
|
|
+ beicepin = beicepin_
|
|
|
+ pici = pici_
|
|
|
+ gsd = gsd_
|
|
|
+ print("打开文件 找到雷达模式,数据集,目标种类相匹配的gsd:", shebei_, beicepin_, pici_, gsd_)
|
|
|
+ break
|
|
|
+ else:
|
|
|
+ pici = os.path.basename(os.path.dirname(source[0]))
|
|
|
+ print("pici: ", pici)
|
|
|
+ beicepin = os.path.basename(os.path.dirname(os.path.dirname(source[0])))
|
|
|
+ print("beicepin: ", beicepin)
|
|
|
+ shebei = os.path.basename(os.path.dirname(os.path.dirname(os.path.dirname(source[0]))))
|
|
|
+ print("shebei: ", shebei)
|
|
|
+ gsd = 0
|
|
|
+ print("over 0")
|
|
|
+ self.shebei.setText(str(shebei))
|
|
|
+ print("over 1")
|
|
|
+ self.beicepin.setText(str(beicepin))
|
|
|
+ print("over 2")
|
|
|
+ self.pici.setText(str(pici))
|
|
|
+ print("over 3")
|
|
|
+ # self.gsd.setText(str(gsd))
|
|
|
+ # print("over 4")
|
|
|
+ else:
|
|
|
+ self.statistic_msg("未选择图片,请重新选取图片路径")
|
|
|
+ # print("self.img_list,source: ", self.img_list,source)
|
|
|
+
|
|
|
+ def open_folder(self):
|
|
|
+ cur_path = os.path.dirname(__file__)
|
|
|
+ last_path = None
|
|
|
+ if os.path.exists(cur_path + "/last_open_dir.txt"):
|
|
|
+ with open(cur_path + "/last_open_dir.txt", "r") as dir_f:
|
|
|
+ lines = dir_f.readlines()
|
|
|
+ if len(lines) > 0:
|
|
|
+ last_path = lines[0]
|
|
|
+ # print("cur_path: ", cur_path)
|
|
|
+ folder = QFileDialog.getExistingDirectory(self, '选择图像文件夹...', last_path if last_path else cur_path)
|
|
|
+
|
|
|
+ if folder != "":
|
|
|
+ # 每次更换文件夹 所有检测结果就清除掉
|
|
|
+ if os.path.exists(self.runs_path):
|
|
|
+ shutil.rmtree(self.runs_path) # os.rmdir(self.runs_path)
|
|
|
+ if os.path.exists(self.runs_path.replace('detect', 'train')):
|
|
|
+ shutil.rmtree(self.runs_path.replace('detect', 'train')) # os.rmdir(self.runs_path)
|
|
|
+ # 显示窗口清除
|
|
|
+ # self.listView.clear()
|
|
|
+ # self.listView.addItem(self.time + "\t软件执行信息")
|
|
|
+ # 计算器清零
|
|
|
+ self.count_D = 0
|
|
|
+ self.DC = str(self.count_D)
|
|
|
+ self.count_T = 0
|
|
|
+
|
|
|
+ self.img_list.clear()
|
|
|
+ # 获取文件夹内的图像列表
|
|
|
+ extenxion = ['/*.jpg', '/*.png', '/*.bmp', '/*.jpeg', '/*.tif', '/*.tiff']
|
|
|
+ for ext in extenxion:
|
|
|
+ self.img_list = self.img_list + glob.glob(folder + ext)
|
|
|
+ self.show_list()
|
|
|
+
|
|
|
+ with open(cur_path + "/last_open_dir.txt", "w") as dir_f:
|
|
|
+ dir_f.writelines(folder)
|
|
|
+ # print("write: ", folder,cur_path+"/last_open_dir.txt")
|
|
|
+
|
|
|
+ self.gsd_file = os.path.join(folder, "gsd.txt")
|
|
|
+ if os.path.exists(self.gsd_file):
|
|
|
+ with open(self.gsd_file, "r", encoding="utf-8") as f:
|
|
|
+ for line in f.readlines():
|
|
|
+ line = line.strip()
|
|
|
+ print("line: ", line)
|
|
|
+ if len(line.split(" ")) == 4:
|
|
|
+ shebei_, beicepin_, pici_, gsd_ = line.split(" ")
|
|
|
+ shebei = shebei_
|
|
|
+ beicepin = beicepin_
|
|
|
+ pici = pici_
|
|
|
+ gsd = gsd_
|
|
|
+ print("打开文件 找到shebei,beicepin,pici相匹配的gsd:", shebei_, beicepin_, pici_, gsd_)
|
|
|
+ break
|
|
|
+ else:
|
|
|
+ pici = os.path.basename(folder)
|
|
|
+ beicepin = os.path.basename(os.path.dirname(folder))
|
|
|
+ shebei = os.path.basename(os.path.dirname(os.path.dirname(folder)))
|
|
|
+ # print(folder, pici, shebei)
|
|
|
+ gsd = 0
|
|
|
+
|
|
|
+ self.shebei.setText(str(shebei))
|
|
|
+ self.beicepin.setText(str(beicepin))
|
|
|
+ self.pici.setText(str(pici))
|
|
|
+ # self.gsd.setText(str(gsd))
|
|
|
+
|
|
|
+ else:
|
|
|
+ self.statistic_msg("未选择图像文件夹,请重新选取图像文件夹")
|
|
|
+ # print("self.img_list,folder: ", self.img_list, folder)
|
|
|
+
|
|
|
+ # update img list
|
|
|
+ def show_list(self):
|
|
|
+
|
|
|
+ self.resultWidget.clear()
|
|
|
+ results = [str(i) for i in self.img_list]
|
|
|
+ self.resultWidget.addItems(results)
|
|
|
+ if len(self.img_list) > 0:
|
|
|
+ self.resultWidget.setCurrentRow(0) # 默认使图像列表中的第一项处于选中状态
|
|
|
+ self.WidgetClicked_default(0)
|
|
|
+
|
|
|
+ def WidgetClicked_default(self, row):
|
|
|
+ item = self.resultWidget.item(row)
|
|
|
+ # 单击触发槽函数
|
|
|
+ # print("WidgetClicked(item): ",item.text())
|
|
|
+ self.statistic_msg("默认选中" + item.text())
|
|
|
+ self.index = self.img_list.index(item.text())
|
|
|
+ self.show_image(item.text(), self.raw_video)
|
|
|
+ self.label_6.setText(f"{os.path.basename(item.text())}原图像")
|
|
|
+
|
|
|
+ def WidgetClicked(self, item):
|
|
|
+ # 单击触发槽函数
|
|
|
+ # print("WidgetClicked(item): ",item,item.text())
|
|
|
+ self.statistic_msg("选中" + item.text())
|
|
|
+ self.index = self.img_list.index(item.text())
|
|
|
+ self.show_image(item.text(), self.raw_video)
|
|
|
+ self.label_6.setText(f"{os.path.basename(item.text())}原图像")
|
|
|
+
|
|
|
+ ########### 系统信息返回 ##########
|
|
|
+ def listViewdoubleClicked(self, item):
|
|
|
+ # 双击触发槽函数
|
|
|
+ try:
|
|
|
+ msg = item.text().split("\t")[1]
|
|
|
+ self.statistic_msg(msg)
|
|
|
+ # 开启查看日志窗口 -- qt打开文件
|
|
|
+ if msg.find("训练") != -1:
|
|
|
+ import re
|
|
|
+ # path 根据 self.count进行筛选
|
|
|
+ c = re.search('\d+', msg).group()
|
|
|
+ if c == "1":
|
|
|
+ s = f'exp'
|
|
|
+ else:
|
|
|
+ s = f'exp{c}'
|
|
|
+ self.logger = LoggerWindow(mode=msg + ' 过程信息',
|
|
|
+ path=os.path.join(self.runs_path.replace('detect', 'train'), s,
|
|
|
+ 'train.txt')) # 新建对象
|
|
|
+ self.logger.ui.show()
|
|
|
+ # self.DC = "0" #我改的
|
|
|
+
|
|
|
+ elif msg.find("检测") != -1:
|
|
|
+ import re
|
|
|
+ # path 根据 self.count进行筛选
|
|
|
+ self.DC = re.search('\d+', msg).group()
|
|
|
+
|
|
|
+ s = f'exp' if self.DC == "1" else f'exp{self.DC}'
|
|
|
+
|
|
|
+ # path 根据 self.count进行筛选
|
|
|
+ # print(os.path.join(self.runs_path,s,'detect.txt'))
|
|
|
+ self.logger = LoggerWindow(mode=msg + ' 过程信息',
|
|
|
+ path=os.path.join(self.runs_path, s, 'detect.txt')) # 新建对象
|
|
|
+ self.logger.ui.show()
|
|
|
+ else:
|
|
|
+ self.statistic_msg("目前没有检测结果,需要先进行检测")
|
|
|
+ # self.DC = "0"
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+
|
|
|
+ def listViewClicked(self, item):
|
|
|
+ # 单击触发槽函数
|
|
|
+ try:
|
|
|
+ msg = item.text().split("\t")[1]
|
|
|
+ self.statistic_msg(msg)
|
|
|
+
|
|
|
+ if msg.find("检测") != -1:
|
|
|
+ import re
|
|
|
+ # path 根据 self.count进行筛选
|
|
|
+ self.DC = re.search('\d+', msg).group()
|
|
|
+ else:
|
|
|
+ self.statistic_msg("目前没有检测结果,需要先进行检测")
|
|
|
+ # self.DC = "0"
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+
|
|
|
+ ################### 纠错 -- Label_click_Mouse #########################
|
|
|
+ # pip install labelme
|
|
|
+ def open_labelme(self):
|
|
|
+ # 开一个进程
|
|
|
+ # 修正命令行
|
|
|
+ try:
|
|
|
+ # if self.DC == "0": #DC是检测次数
|
|
|
+ # self.labelme_thread.det_path = "" #os.path.join(self.runs_path,s,"jsons") #标注文件替换
|
|
|
+
|
|
|
+ if self.DC == "0":
|
|
|
+ self.statistic_msg("目前没有检测结果,需要先进行检测")
|
|
|
+ else:
|
|
|
+ s = f'exp' if self.DC == "1" else f'exp{self.DC}'
|
|
|
+ det_path = os.path.join(self.runs_path, s, "jsons")
|
|
|
+ src_path = os.path.dirname(self.img_list[0])
|
|
|
+ json_list = glob.glob(src_path + '/*.json') # 先将原来标注好的json文件改为gt_.json文件
|
|
|
+ has_gt = False
|
|
|
+ for jsonf in json_list:
|
|
|
+ extenxion = ['.jpg', '.png', '.bmp', '.jpeg', '.tif', '.tiff']
|
|
|
+ for ext in extenxion:
|
|
|
+ imgf = jsonf.replace(".json", ext)
|
|
|
+ if os.path.exists(imgf):
|
|
|
+ has_gt = True
|
|
|
+ break
|
|
|
+ if has_gt:
|
|
|
+ break
|
|
|
+ if has_gt:
|
|
|
+ MessageBox(self.closeButton, title='提示', text='该图片所在目录下标注文件,不能执行纠错操作',
|
|
|
+ time=1000, auto=True).exec_()
|
|
|
+ else:
|
|
|
+
|
|
|
+ self.labelme_thread.det_path = det_path
|
|
|
+ self.labelme_thread.src_path = src_path
|
|
|
+ self.labelme_thread.start()
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+
|
|
|
+ ############################ 结果导出 #######################################
|
|
|
+ def save_output(self): # 导出报表,调用WriteThread
|
|
|
+ try:
|
|
|
+ if self.DC == "0":
|
|
|
+ self.statistic_msg("目前没有检测结果,需要先进行检测")
|
|
|
+ else:
|
|
|
+ # 书写 excel 内容
|
|
|
+ msg = f"第{self.DC}次检测结果"
|
|
|
+ # if os.name == 'nt': #windows 系统
|
|
|
+ file_path, _ = QFileDialog.getSaveFileName(self, "保存 结果", ".", 'Excel(*.xlsx)')
|
|
|
+ if file_path != "":
|
|
|
+ # 传入参数 开启进程
|
|
|
+ self.write_thread.msg = msg
|
|
|
+ self.write_thread.imgs = self.img_list
|
|
|
+ self.write_thread.path = file_path
|
|
|
+
|
|
|
+ s = f'exp' if self.DC == "1" else f'exp{self.DC}'
|
|
|
+ scr = os.path.join(self.runs_path, s)
|
|
|
+
|
|
|
+ self.write_thread.src = scr
|
|
|
+ self.write_thread.gsd = float(self.gsd.text())
|
|
|
+ self.write_thread.start()
|
|
|
+ # self.write_xlsxwriter(msg,self.img_list,file_path)
|
|
|
+ # else: # os.name = posix
|
|
|
+ # file_path, _ = QFileDialog.getSaveFileName(self,"保存 结果",".",'txt(*.txt)')
|
|
|
+ # self.write_txt(msg,self.img_list,file_path)
|
|
|
+
|
|
|
+ ################
|
|
|
+ else:
|
|
|
+ self.statistic_msg("未选择报表文件,请重新选取报表路径")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+
|
|
|
+ def write_xlsxwriter(self, msg):
|
|
|
+
|
|
|
+ if msg == "error":
|
|
|
+ self.statistic_msg(msg)
|
|
|
+ self.tips.close()
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示', text='结果导出错误,请重新导出', time=1000, auto=True).exec_()
|
|
|
+ self.show_image(self.plots_thread.img_name, self.out_video)
|
|
|
+
|
|
|
+ elif msg == "开始导出报表":
|
|
|
+ self.statistic_msg(msg)
|
|
|
+ self.tips = MessageBox(
|
|
|
+ self.closeButton, title='提示', text=f'请稍等,正在导出图表文件。。。', time=500, auto=False) # .exec_()
|
|
|
+ self.tips.show()
|
|
|
+
|
|
|
+ elif msg == "导出报表完成":
|
|
|
+ self.statistic_msg(msg)
|
|
|
+ self.tips.close()
|
|
|
+
|
|
|
+ if self.DC != "0":
|
|
|
+ MessageBox(
|
|
|
+ self.closeButton, title='提示',
|
|
|
+ text=f'第{self.DC}次检测结果图表格式文件导出成功,正在关闭提示窗口。。。', time=2000, auto=True).exec_()
|
|
|
+ else:
|
|
|
+ self.statistic_msg("目前没有检测结果,需要先进行检测")
|
|
|
+
|
|
|
+ ############################## 模型操作 ##############################
|
|
|
+ def begin_action(self):
|
|
|
+ if self.t_flag == 2:
|
|
|
+ self.model_type = os.path.join(self.model_path, self.comboBox.currentText())
|
|
|
+ # self.model_type = os.path.join(self.model_path,"yolov5s-seg.pt")#我改为训练只使用预训练模型
|
|
|
+ self.train_thread.model = self.model_type
|
|
|
+ # 模型训练 ##
|
|
|
+ if self.ui_train.radioButton_3.isChecked(): # 增加样本再训练
|
|
|
+ if len(self.img_list):
|
|
|
+ save_dirs = Path('./dataset') # fix path
|
|
|
+ Path(save_dirs / 'labels').mkdir(parents=True, exist_ok=True)
|
|
|
+ Path(save_dirs / 'images').mkdir(parents=True, exist_ok=True)
|
|
|
+ for img_name in self.img_list:
|
|
|
+ dirs = os.path.dirname(img_name)
|
|
|
+ name = os.path.basename(img_name)
|
|
|
+ jsons = name.replace(name.split('.')[-1], 'json')
|
|
|
+ if os.path.exists(os.path.join(dirs, jsons)):
|
|
|
+ # 转换格式 -- 摸一个文件夹
|
|
|
+ self.statistic_msg(f"{img_name}图像成功加入训练集中")
|
|
|
+ convert_json_label_to_yolov_seg_label(os.path.join(dirs, jsons), dirs,
|
|
|
+ os.path.join(str(save_dirs / 'labels'),
|
|
|
+ jsons.replace(".json", ".txt")))
|
|
|
+ shutil.copyfile(img_name, os.path.join(str(save_dirs / 'images'), name))
|
|
|
+ else:
|
|
|
+ self.statistic_msg(f"{img_name}图像没有对应的标注文件,请先进行检测或人工标注后再训练模型")
|
|
|
+ # 解除冻结
|
|
|
+
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ return None
|
|
|
+ else:
|
|
|
+ self.statistic_msg(f"请选择要新增加的图像数据")
|
|
|
+ # 解除冻结
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ return None
|
|
|
+
|
|
|
+ self.count_T += 1
|
|
|
+ self.train_thread.start() # 启动子进程 -- 注意命令行的中 python 解释器 的选择 传递一个结束信号
|
|
|
+
|
|
|
+ self.statistic_msg(f"第{self.count_T}次训练中")
|
|
|
+ # 解除冻结
|
|
|
+ self.trainbutton.setEnabled(True)
|
|
|
+ if self.t_flag == 0:
|
|
|
+ self.model_type = os.path.join(self.model_path, self.comboBox.currentText())
|
|
|
+ self.det_thread.model = self.model_type
|
|
|
+ try:
|
|
|
+ if len(self.img_list) == 0:
|
|
|
+ self.statistic_msg("目前没有检测结果,需要先进行检测")
|
|
|
+ return
|
|
|
+ elif len(self.img_list) > 1:
|
|
|
+ self.det_thread.data = os.path.dirname(self.img_list[0])
|
|
|
+ else:
|
|
|
+ self.det_thread.data = self.img_list[0]
|
|
|
+
|
|
|
+ self.count_D += 1
|
|
|
+ self.det_thread.start() # 启动子进程 -- 注意命令行的中 python 解释器 的选择
|
|
|
+ self.statistic_msg(f"第{self.count_D}次检测中")
|
|
|
+ self.DC = str(self.count_D)
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+
|
|
|
+ self.detectbutton.setEnabled(True)
|
|
|
+
|
|
|
+ if self.t_flag == 1:
|
|
|
+ self.py_type = os.path.join(self.feature_alg, self.comboBox.currentText())
|
|
|
+ self.fea_thread.py = self.py_type
|
|
|
+ # self.fea_thread.send_msg.connect(lambda x: self.show_msg3(x))
|
|
|
+ try:
|
|
|
+ if len(self.img_list) == 0:
|
|
|
+ self.statistic_msg("目前没有识别结果,需要先进行识别")
|
|
|
+ return
|
|
|
+ elif len(self.img_list) > 1:
|
|
|
+ self.fea_thread.data = os.path.dirname(self.img_list[0])
|
|
|
+ else:
|
|
|
+ self.fea_thread.data = self.img_list[0]
|
|
|
+
|
|
|
+ self.count_D += 1
|
|
|
+ self.fea_thread.start() # 启动子进程 -- 注意命令行的中 python 解释器 的选择
|
|
|
+
|
|
|
+ script_path = self.py_type
|
|
|
+
|
|
|
+ image_path_list = self.img_list
|
|
|
+
|
|
|
+ # 循环遍历选择的文件夹内的图片,并传入所选算法中
|
|
|
+
|
|
|
+ self.statistic_msg("处理中......")
|
|
|
+ for path in image_path_list:
|
|
|
+ result = subprocess.run(['python', script_path, path], capture_output=True, text=True)
|
|
|
+ self.output_file_list[path] = result.stdout.replace('\n', '')
|
|
|
+
|
|
|
+ self.statistic_msg(f"处理完成!")
|
|
|
+
|
|
|
+ # self.statistic_msg(f"第{self.count_D}次识别中")
|
|
|
+ self.DC = str(self.count_D)
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+
|
|
|
+ self.trainbutton_2.setEnabled(True)
|
|
|
+
|
|
|
+
|
|
|
+ if self.t_flag == 3 or self.t_flag == 4:
|
|
|
+ self.py_type = os.path.join(self.feature_alg, self.comboBox.currentText())
|
|
|
+ self.fea_thread.py = self.py_type
|
|
|
+
|
|
|
+ try:
|
|
|
+ if len(self.img_list) == 0:
|
|
|
+ self.statistic_msg("目前没有识别结果,需要先进行识别")
|
|
|
+ return
|
|
|
+ elif len(self.img_list) > 1:
|
|
|
+ self.fea_thread.data = os.path.dirname(self.img_list[0])
|
|
|
+ else:
|
|
|
+ self.fea_thread.data = self.img_list[0]
|
|
|
+
|
|
|
+ self.count_D += 1
|
|
|
+ self.fea_thread.start() # 启动子进程 -- 注意命令行的中 python 解释器 的选择
|
|
|
+
|
|
|
+ script_path = self.py_type
|
|
|
+
|
|
|
+ img_list = self.img_list
|
|
|
+
|
|
|
+ my_path_pic_0 = [os.path.dirname(path) for path in img_list]
|
|
|
+ my_path_pic = my_path_pic_0[0]
|
|
|
+
|
|
|
+ label_path = os.path.dirname(my_path_pic)
|
|
|
+
|
|
|
+ my_label_path = os.path.join(label_path, "picture_labels")
|
|
|
+ print(my_label_path)
|
|
|
+
|
|
|
+ label_path_list = os.listdir(my_label_path)
|
|
|
+ self.statistic_msg(f"开始检测")
|
|
|
+ for img_path in img_list:
|
|
|
+ img_name = os.path.basename(img_path).split('.')[0]
|
|
|
+ for label in label_path_list:
|
|
|
+ label_name = os.path.basename(label).split('.')[0]
|
|
|
+ if img_name == label_name:
|
|
|
+ s_label_path = os.path.join(my_label_path, label)
|
|
|
+ result = subprocess.run(['python', script_path, img_path, s_label_path], capture_output=True, text=True)
|
|
|
+ self.output_file_list[img_path] = result.stdout.replace('\n', '')
|
|
|
+ break
|
|
|
+
|
|
|
+ self.statistic_msg(f"第{self.count_D}次识别中")
|
|
|
+ self.DC = str(self.count_D)
|
|
|
+ except Exception as e:
|
|
|
+ self.statistic_msg(repr(e))
|
|
|
+
|
|
|
+ self.trainbutton_3.setEnabled(True)
|
|
|
+
|
|
|
+
|
|
|
+import configparser
|
|
|
+
|
|
|
+
|
|
|
+# ## 密码登录
|
|
|
+class Welcome(QMainWindow):
|
|
|
+ success = pyqtSignal(bool) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ super(Welcome, self).__init__()
|
|
|
+ self.login = loadUi('qt_win/login-form2.ui')
|
|
|
+ self.register = loadUi('qt_win/register-form2.ui')
|
|
|
+
|
|
|
+ self.login.setWindowTitle("登陆界面")
|
|
|
+ self.register.setWindowTitle("注册界面")
|
|
|
+ self.login.b1.clicked.connect(self.Login)
|
|
|
+ self.login.b2.clicked.connect(self.show_reg)
|
|
|
+ self.register.b3.clicked.connect(self.reg)
|
|
|
+ self.register.b4.clicked.connect(self.show_login)
|
|
|
+ self.IsRememberUser()
|
|
|
+ self.config_json = "config.json" # 保存信息的json文件
|
|
|
+
|
|
|
+ '''读取json -- 解析账号'''
|
|
|
+
|
|
|
+ def get_information(self):
|
|
|
+ if not os.path.exists(self.config_json):
|
|
|
+ return 0
|
|
|
+ with open(self.config_json, 'r') as f:
|
|
|
+ json_data = json.load(f)
|
|
|
+ users = json_data.keys()
|
|
|
+ if self.account not in users:
|
|
|
+ return 0
|
|
|
+ if self.passwd != json_data[self.account]:
|
|
|
+ return 1
|
|
|
+ else:
|
|
|
+ return 2
|
|
|
+
|
|
|
+ '''读写json -- 解析账号'''
|
|
|
+
|
|
|
+ def set_information(self):
|
|
|
+ if not os.path.exists(self.config_json):
|
|
|
+ # 不存在 新建
|
|
|
+ data = {} # key -- id ; value -- password
|
|
|
+ else:
|
|
|
+ # 读取
|
|
|
+ with open(self.config_json, 'r') as f:
|
|
|
+ data = json.load(f)
|
|
|
+ # 判断
|
|
|
+ if self.re_account in data.keys():
|
|
|
+ return 0
|
|
|
+ if self.re_passwd != self.confirm_passwd:
|
|
|
+ return 1
|
|
|
+ # 写入文件
|
|
|
+ data[self.re_account] = self.re_passwd
|
|
|
+ with open(self.config_json, "w") as f:
|
|
|
+ json.dump(data, f)
|
|
|
+ return 2
|
|
|
+
|
|
|
+ """设置记住密码"""
|
|
|
+
|
|
|
+ def IsRememberUser(self):
|
|
|
+ config = configparser.ConfigParser()
|
|
|
+ if not os.path.exists('user.ini'):
|
|
|
+ self.login.c.setChecked(False)
|
|
|
+ else:
|
|
|
+ file = config.read('user.ini') # 读取密码账户的配置文件
|
|
|
+ config_dict = config.defaults() # 返回包含实例范围默认值的字典
|
|
|
+
|
|
|
+ if config_dict['remember'] == 'True': # 如果user.ini文件存在,并且里面设置了记住密码,就将默认的用户名和密码取出来,自动填充在用户界面上
|
|
|
+ self.account = config_dict['user_name'] # 获取账号信息
|
|
|
+ self.login.tb1.setText(self.account) # 写入账号上面
|
|
|
+ self.passwd = config_dict['password']
|
|
|
+ self.login.tb2.setText(self.passwd)
|
|
|
+ self.login.c.setChecked(True)
|
|
|
+ else:
|
|
|
+ self.login.tb1.setText("")
|
|
|
+ self.login.tb2.setText("")
|
|
|
+ self.login.c.setChecked(False)
|
|
|
+
|
|
|
+ """设置配置文件格式,设置默认用户名和密码,登录成功后,将用户填写的用户名和密码写入user.ini文件中"""
|
|
|
+
|
|
|
+ def config_ini(self):
|
|
|
+ self.account = self.login.tb1.text()
|
|
|
+ self.passwd = self.login.tb2.text()
|
|
|
+ config = configparser.ConfigParser()
|
|
|
+ if self.login.c.isChecked():
|
|
|
+ config["DEFAULT"] = {
|
|
|
+ "user_name": self.account,
|
|
|
+ "password": self.passwd,
|
|
|
+ "remember": self.login.c.isChecked()
|
|
|
+ }
|
|
|
+ else:
|
|
|
+ config["DEFAULT"] = {
|
|
|
+ "user_name": self.account,
|
|
|
+ "password": "",
|
|
|
+ "remember": self.login.c.isChecked()
|
|
|
+ }
|
|
|
+ with open('user.ini', 'w') as configfile:
|
|
|
+ config.write((configfile))
|
|
|
+
|
|
|
+ def Login(self):
|
|
|
+
|
|
|
+ self.account = self.login.tb1.text()
|
|
|
+ self.passwd = self.login.tb2.text()
|
|
|
+
|
|
|
+ if len(self.account) == 0 or len(self.passwd) == 0:
|
|
|
+ MessageBox(
|
|
|
+ self.login, title='提示', text='用户名或密码为空!', time=2000, auto=True).exec_()
|
|
|
+ else:
|
|
|
+ info = self.get_information()
|
|
|
+ if info == 0:
|
|
|
+ MessageBox(
|
|
|
+ self.login, title='提示', text='此用户不存在!', time=2000, auto=True).exec_()
|
|
|
+ # QMessageBox.information(self,"Login Output","Invalid User, Register for new user")
|
|
|
+ elif info == 1:
|
|
|
+ MessageBox(
|
|
|
+ self.login, title='提示', text='用户名和密码不匹配!', time=2000, auto=True).exec_()
|
|
|
+ # QMessageBox.information(self,"Login Output","Error! User and password do not match")
|
|
|
+ else:
|
|
|
+ MessageBox(
|
|
|
+ self.login, title='提示', text='恭喜!登录成功!', time=2000, auto=True).exec_()
|
|
|
+ # QMessageBox.information(self,"Login Output","Congrats! you login successfully")
|
|
|
+ self.config_ini() # 加载用户密码配置文件
|
|
|
+ # 关闭窗口
|
|
|
+ self.login.close()
|
|
|
+ self.success.emit(True)
|
|
|
+
|
|
|
+ # 注册
|
|
|
+ def show_reg(self):
|
|
|
+ self.login.close()
|
|
|
+ self.register.tb3.setText("") #
|
|
|
+ self.register.tb4.setText("") #
|
|
|
+ self.register.tb5.setText("") #
|
|
|
+ self.register.show()
|
|
|
+
|
|
|
+ def reg(self):
|
|
|
+ self.re_account = self.register.tb3.text()
|
|
|
+ self.re_passwd = self.register.tb4.text()
|
|
|
+ self.confirm_passwd = self.register.tb5.text()
|
|
|
+ if len(self.re_account) == 0 or len(self.re_passwd) == 0 or len(self.confirm_passwd) == 0:
|
|
|
+ MessageBox(
|
|
|
+ self.register, title='提示', text='用户名或密码为空!', time=2000, auto=True).exec_()
|
|
|
+ else:
|
|
|
+ info = self.set_information()
|
|
|
+ if info == 0:
|
|
|
+ MessageBox(
|
|
|
+ self.register, title='提示', text='此用户名已经存在了!', time=2000, auto=True).exec_()
|
|
|
+ # QMessageBox.information(self,"Register Output", "The user already registered, Try another username")
|
|
|
+ elif info == 1:
|
|
|
+ MessageBox(
|
|
|
+ self.register, title='提示', text='两次密码不一致!', time=2000, auto=True).exec_()
|
|
|
+ # QMessageBox.information(self,"Register Output", "Error! The passwords entered twice do not match")
|
|
|
+ else:
|
|
|
+ MessageBox(
|
|
|
+ self.register, title='提示', text='注册成功,现在可以登录了!', time=2000, auto=True).exec_()
|
|
|
+ # QMessageBox.information(self,"Register Output", "The user registered successfully, You can login now!!!")
|
|
|
+ self.show_login() # 返回登录
|
|
|
+
|
|
|
+ def show_login(self):
|
|
|
+ self.register.close()
|
|
|
+ self.login.tb1.setText("") #
|
|
|
+ self.login.tb2.setText("") #
|
|
|
+ self.login.show()
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ app = QApplication(sys.argv)
|
|
|
+ # wel = Welcome()
|
|
|
+ # wel.login.show()
|
|
|
+ win = Window()
|
|
|
+ # win.show()
|
|
|
+ sys.exit(app.exec_())
|