qt_main.py 100 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223
  1. ##############功能测试##################
  2. import ctypes
  3. from shapely.geometry import Polygon
  4. from shapely.ops import unary_union
  5. # whnd = ctypes.windll.kernel32.GetConsoleWindow()
  6. # if whnd != 0:
  7. # ctypes.windll.user32.ShowWindow(whnd, 0)
  8. # ctypes.windll.kernel32.CloseHandle(whnd)
  9. import sys
  10. import os
  11. import subprocess
  12. import time
  13. import cv2
  14. import shutil
  15. from pathlib import Path
  16. from split_train_val_test import split
  17. from crop import crop
  18. from json_to_yolo import json_to_yolo
  19. from datetime import datetime
  20. from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QPushButton, QWidget
  21. FILE = Path(__file__).resolve()
  22. ROOT = FILE.parents[0] # YOLOv5 root directory
  23. if str(ROOT) not in sys.path:
  24. sys.path.append(str(ROOT)) # add ROOT to PATH
  25. ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
  26. from PyQt5.QtGui import QPixmap
  27. from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QButtonGroup, QMessageBox, QListView, \
  28. QAbstractItemView, QTreeView, QWidget, QVBoxLayout
  29. from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal, QDateTime
  30. from qt_win.win3 import Ui_mainWindow, MessageBox, InfoMessageBox
  31. from PyQt5.uic import loadUi
  32. import apprcc_rc
  33. import glob
  34. from utils.segment.dataloaders import polygons2masks # 用来重新画修正后的图片
  35. from utils.plots import Annotator, colors
  36. from name import CT_name_zh, CT_name # 演示用coco 后续换成
  37. import json
  38. import numpy as np
  39. import math
  40. from PIL import Image, ImageDraw, ImageFont
  41. os.environ["GIT_PYTHON_REFRESH"] = "quiet"
  42. os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
  43. import psutil
  44. """
  45. 通过进程名杀死进程
  46. taskkill /F /IM explorer.exe
  47. """
  48. def kill_name(name):
  49. pids = psutil.pids()
  50. for pid in pids:
  51. p = psutil.Process(pid)
  52. if p.name() == name:
  53. if os.name == 'nt':
  54. cmd = 'taskkill /pid ' + str(pid) + ' /f'
  55. try:
  56. print(pid, 'killed')
  57. os.system(cmd)
  58. except Exception as e:
  59. print(e)
  60. elif os.name == 'posix':
  61. # Linux系统
  62. cmd = 'kill ' + str(pid)
  63. try:
  64. print(pid, 'killed')
  65. os.system(cmd)
  66. except Exception as e:
  67. print(e)
  68. def component_polygon_area(poly):
  69. """Compute the area of a component of a polygon.
  70. Args:
  71. x (ndarray): x coordinates of the component
  72. y (ndarray): y coordinates of the component
  73. Return:
  74. float: the are of the component
  75. """
  76. # poly = poly.numpy()
  77. x = poly[:, 0]
  78. y = poly[:, 1]
  79. return 0.5 * np.abs(
  80. np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))) # np.roll 意即“滚动”,类似移位操作
  81. # 注意这里的np.dot表示一维向量相乘
  82. def component_polygon_Circle(poly):
  83. """Compute the area of a component of a polygon.
  84. """
  85. # poly = poly.numpy()
  86. poly = poly[:, None, :]
  87. # poly .shpae (n,1,2)
  88. _, radius = cv2.minEnclosingCircle(poly)
  89. return 2 * math.pi * radius
  90. def component_polygon_Circle_max(src, poly):
  91. # Calculate the distances to the contour
  92. box = cv2.boundingRect(poly[:, None, :])
  93. x, y, w, h = box
  94. raw_dist = np.empty(src.shape[:2])
  95. mask = np.zeros(src.shape, dtype=np.int32)
  96. cv2.fillPoly(mask, [poly.astype(np.int32)], color=(255, 255, 255)) # 在大图上画该类的所有检测多边形
  97. for i in range(x, x + w, ):
  98. for j in range(y, y + h, ):
  99. if mask[j, i, 0] == 255:
  100. raw_dist[j, i] = cv2.pointPolygonTest(poly[:, None:, ], (i, j), True)
  101. # 获取最大值即内接圆半径,中心点坐标
  102. _, maxVal, _, maxLoc = cv2.minMaxLoc(raw_dist)
  103. return maxVal * 2, maxLoc
  104. det_img = None
  105. def plots_new_one(json_file, img_name, out_path, flag=True): # 计算缺陷信息并展示
  106. """
  107. 画单张图片 默认是未纠错模式
  108. """
  109. s_t = time.time()
  110. # print("s_t: ", s_t)
  111. im0 = cv2.imread(img_name) # BGR
  112. s2_t = time.time()
  113. # print("time2: ", s2_t-s_t)
  114. # Mask plotting
  115. segments = []
  116. class_id = []
  117. class_name = []
  118. # ccn = {}
  119. new_dict = {v: k for k, v in CT_name.items()}
  120. # print("plots_new_one, json_file: ",json_file)
  121. if os.path.exists(json_file):
  122. with open(json_file, 'r') as f:
  123. data = json.load(f)
  124. class_number = data['shapes'] # 类型个数
  125. for cn in class_number:
  126. if cn['label'] in list(CT_name_zh.keys()):
  127. # segments.append(np.array(cn['points'],dtype=np.float32)) #获取每个轮廓点
  128. segments.append(np.array(cn['points'], dtype=np.int32))
  129. class_name.append(CT_name_zh[cn['label']]) # 获取对应的class name
  130. class_id.append(new_dict[cn['label']]) # 获取对应的class id
  131. # bboxs.append(np.array(cn['bbox'])) #x[0] y[0] 左上角点 用于画图 和显示信息
  132. # ccn = data
  133. # print("segments: ", segments[0].dtype)
  134. s3_t = time.time()
  135. # print("time3: ", s3_t - s2_t)
  136. nc = len(class_name)
  137. if nc > 0:
  138. s3_1_t = time.time()
  139. # print("time3_1: ", s3_1_t - s2_t)
  140. annotator = Annotator(im0, line_width=1, example=str(CT_name)) #
  141. # 二值化
  142. # masks = polygons2masks(im0.shape[:2], segments, color=1)
  143. s3_2_t = time.time()
  144. # print("time3_2: ", s3_2_t - s3_1_t)
  145. # 重新由轮廓点 求取信息 因为有可能更新
  146. p_co = [colors(x, True) for x in class_id]
  147. # annotator.masks_cpu( #cpu上
  148. # masks,
  149. # colors=p_co,
  150. # im_gpu=im0.transpose(2, 0, 1) / 255)
  151. s3_3_t = time.time()
  152. # print("time3_3: ", s3_3_t - s3_2_t)
  153. # im0 = annotator.result()
  154. s4_t = time.time()
  155. # print("time4: ", s4_t - s3_t)
  156. alpha = 0.75
  157. mask = np.zeros((im0.shape[0], im0.shape[1]), dtype=np.uint8)
  158. overlay = im0.copy()
  159. cv2.fillPoly(mask, pts=segments, color=(255, 255, 255))
  160. cv2.fillPoly(im0, pts=segments, color=(125, 255, 0))
  161. cv2.addWeighted(overlay, alpha, im0, 1 - alpha, 0, im0)
  162. print("flag: ", flag)
  163. for i in range(nc):
  164. txt_color = p_co[i] # cocoors(new_dict[list(ccn.keys())[i]], True)
  165. # x, y, _, _ = cv2.boundingRect(segments[i][:,None:,])
  166. # print("segments: ",segments[i].shape) #(754, 2)
  167. xy_index = np.argmin(segments[i], axis=0) # 其中,axis=1表示按行计算
  168. # print("xy_index.shape,xy_index: ",xy_index.shape,xy_index) #(2,) [ 139.55 267.14]
  169. textSize = int(0.04 * im0.shape[0])
  170. x = segments[i][xy_index[1], 0]
  171. y = segments[i][xy_index[1], 1]
  172. if y - textSize <= 10:
  173. y = 10
  174. # print("im0.shape[0]: ", im0.shape[0], x, y)
  175. def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=0.04):
  176. # print("textColor: ", type(textColor), tuple(list(textColor)[::-1]))
  177. textColor = tuple(list(textColor)[::-1])
  178. if (isinstance(img, np.ndarray)): # 判断是否OpenCV图片类型
  179. img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
  180. # print("img.size: ", img.size)
  181. # 创建一个可以在给定图像上绘图的对象
  182. draw = ImageDraw.Draw(img)
  183. # 字体的格式
  184. # fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")
  185. fontStyle = ImageFont.truetype("simhei.ttf", textSize, encoding="utf-8")
  186. # 绘制文本
  187. draw.text(position, text, textColor, font=fontStyle)
  188. # 转换回OpenCV格式
  189. return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
  190. # cv2.putText(im0,class_name[i],(int(x), int(y)), 0, 0.75, txt_color, thickness=2 ,lineType=cv2.LINE_AA)
  191. if not flag:
  192. pass
  193. # im0 = cv2AddChineseText(im0, f"{class_name[i]}", (int(x), int(y)), txt_color, textSize)
  194. else:
  195. # 计算面积
  196. area = round(cv2.contourArea(segments[i]), 2)
  197. lenth = round(cv2.arcLength(segments[i], True) / 2, 2)
  198. # 计算宽度
  199. width, maxLoc = component_polygon_Circle_max(im0, segments[i])
  200. im0 = cv2AddChineseText(im0, f"{class_name[i]}({int(area)}mm²,{int(lenth)}mm,{int(width)}mm)",
  201. (int(x), int(y + 10)), txt_color, textSize) # 只显示面积
  202. s5_t = time.time()
  203. print("time5: ", s5_t - s4_t)
  204. # global det_img
  205. # det_img=im0
  206. cv2.imwrite(os.path.join(out_path, os.path.basename(img_name)), im0)
  207. # labelme 2 yolov5
  208. def convert_json_label_to_yolov_seg_label(json_file, root_path, save_path):
  209. # print(json_file)
  210. f = open(json_file)
  211. json_info = json.load(f)
  212. # print(json_info.keys())
  213. img = cv2.imread(os.path.join(root_path, json_info["imagePath"]))
  214. height, width, _ = img.shape
  215. np_w_h = np.array([[width, height]], np.int32)
  216. txt_file = save_path # json_file.replace(".json", ".txt")
  217. f = open(txt_file, "a")
  218. for point_json in json_info["shapes"]:
  219. txt_content = ""
  220. np_points = np.array(point_json["points"], np.int32)
  221. norm_points = np_points / np_w_h
  222. norm_points_list = norm_points.tolist()
  223. txt_content += "0 " + " ".join([" ".join([str(cell[0]), str(cell[1])]) for cell in norm_points_list]) + "\n"
  224. f.write(txt_content)
  225. class TrainThread(QThread): # 调用train_qt.py
  226. send_msg = pyqtSignal(str) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
  227. # send_percent = pyqtSignal(int) #
  228. def __init__(self):
  229. super(TrainThread, self).__init__()
  230. self.model = " "
  231. self.Work = ["python.exe", "train_qt.py"] # os后台执行命令行 内容
  232. self.Command = []
  233. def getAllImage(self, folderPath, imageList):
  234. extend_name = ["jpg", "jpeg", "png", "bmp", "tif", "tiff"]
  235. # exclude_dir=["data_crop","data_yolo","data_original"]
  236. # exclude_dir = ["data_crop", "data_yolo", "data_debug","val","eval","eval_debug"]
  237. if os.path.isfile(folderPath):
  238. basename = os.path.basename(folderPath)
  239. ext = basename.rsplit(".")[-1]
  240. bsname = basename.rsplit(".", 1)[0]
  241. len1 = len(folderPath) - len(ext)
  242. json_f = folderPath[:len1] + "json"
  243. if ext in extend_name:
  244. if os.path.exists(json_f):
  245. imageList.append(folderPath)
  246. return imageList
  247. else:
  248. for item in os.listdir(folderPath):
  249. subFolderPath = os.path.join(folderPath, item)
  250. self.getAllImage(subFolderPath, imageList)
  251. return imageList
  252. def run(self):
  253. try:
  254. self.send_msg.emit('开始训练')
  255. # 先准备好yolo格式的数据集
  256. data_paths = self.Command[-1]
  257. print("data_paths: ", data_paths)
  258. imageList = []
  259. data_paths_ = data_paths.strip().split(";")
  260. print("data_paths_: ", data_paths_)
  261. for data_path in data_paths_:
  262. if len(data_path) > 0:
  263. self.getAllImage(data_path, imageList) # 获取所有原始大图
  264. print("imageList: ", imageList)
  265. split(imageList) # 划分train-val-test
  266. crop()
  267. json_to_yolo()
  268. info = self.Work
  269. if self.Command[0] == "True":
  270. info += ["--weights"] + [self.model]
  271. info += ["--hyp"] + [self.Command[-3]] + ["--imgsz"] + [self.Command[1]] \
  272. + ["--epochs"] + [self.Command[2]] + ["--batch-size"] + [self.Command[3]] \
  273. + ["--ckptname"] + [self.Command[-4]] + ["--project"] + [self.Command[-2]]
  274. # print(info)
  275. # os.system(info)
  276. subprocess.run(info)
  277. self.send_msg.emit('训练结束')
  278. # self.statistic_label.clear()
  279. except Exception as e:
  280. print('开始训练 %s' % e)
  281. self.send_msg.emit('%s' % e)
  282. # 识别
  283. class DetThread(QThread): # 检测类,调用predict_largepic.py
  284. send_msg = pyqtSignal(str) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
  285. def __init__(self):
  286. super(DetThread, self).__init__()
  287. self.model = ""
  288. self.Work = ["python.exe", "predict_qt.py"] # os后台执行命令行 内容
  289. self.Work2 = ["python.exe", "predict_largepic.py"] # os后台执行命令行 内容
  290. self.data = None # 默认
  291. self.split = False # 默认 不 切片
  292. def run(self):
  293. try:
  294. if self.split:
  295. info = self.Work2 + ["--batch-size"] + [self.Command[3]]
  296. else:
  297. info = self.Work
  298. info += ["--weights"] + [self.model]
  299. info += ["--imgsz"] + [self.Command[0]] \
  300. + ["--conf-thres"] + [self.Command[2]] + ["--batch-size"] + ["1"] \
  301. + ["--iou-thres"] + [self.Command[1]] + ["--project"] + [self.Command[-1]]
  302. if self.data:
  303. info += ["--source"] + [self.data]
  304. # print(info)
  305. self.send_msg.emit('开始检测')
  306. print("*********************************************************")
  307. print("DetThread info: ", info)
  308. # os.system(info)
  309. # subprocess.Popen(info) #这样不阻塞
  310. subprocess.run(info) # 这样阻塞
  311. self.send_msg.emit('检测结束')
  312. # self.statistic_label.clear()
  313. except Exception as e:
  314. print('%s' % e)
  315. self.send_msg.emit('%s' % e)
  316. class FeaThread(QThread):
  317. send_msg = pyqtSignal(str) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
  318. def __init__(self):
  319. super().__init__()
  320. self.py = ""
  321. self.data = None
  322. #
  323. # def run(self):
  324. # self.send_msg.emit("s")
  325. class JianThread(QThread):
  326. send_msg = pyqtSignal(str) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
  327. def __init__(self):
  328. super().__init__()
  329. self.py = ""
  330. self.data = None
  331. class LabelThread(QThread): # 调用labelme第三方库
  332. def __init__(self):
  333. super(LabelThread, self).__init__()
  334. # labelme 必须找到入口文件
  335. self.Work = "python.exe D:\\Anaconda3\\envs\\pyqt\\Lib\site-packages\\labelme\\__main__.py --labels ./labels.txt" # ./res/ --labels ./dataset/labels.txt" # os后台执行命令行 内容
  336. self.Command = []
  337. self.src_path = "" # 是检测图片所在路径,也就是标注文件所在路径
  338. self.det_path = "" # 是检测结果json文件的路径
  339. self.cor_path = "D:/data/correct"
  340. def run(self):
  341. try:
  342. info = self.Work
  343. info += " " + self.src_path
  344. # print(info)
  345. # copy 检测结果的 至
  346. max_i = 0
  347. if not os.path.exists(self.cor_path):
  348. os.mkdir(self.cor_path)
  349. max_i = 0
  350. else:
  351. cor_json_list = glob.glob(self.cor_path + '/*.json') # 再将现在检测好的json文件移到图片所在文件夹下
  352. if len(cor_json_list) == 0:
  353. max_i = 0
  354. else:
  355. cor_json_list = sorted(cor_json_list, key=lambda x: int(os.path.basename(x)[:-5]),
  356. reverse=True) # 降序
  357. print("cor_json_list: ", cor_json_list)
  358. max_i = int((os.path.basename(cor_json_list[0])[:-5]))
  359. print("max_i: ", max_i)
  360. json_list = glob.glob(self.det_path + '/*.json') # 再将现在检测好的json文件移到图片所在文件夹下
  361. # 记录下检测文件的修改时间
  362. time_d = {}
  363. for jsons in json_list:
  364. name = os.path.basename(jsons)
  365. # if os.path.exists(os.path.join(self.src_path, name)):
  366. # shutil.move(os.path.join(self.src_path, name), os.path.join(self.src_path, "gt_"+name)) #避免检测结果的json文件覆盖掉标注的json文件
  367. shutil.copyfile(jsons, os.path.join(self.src_path,
  368. name)) # 把检测结果json文件复制到检测图像所在文件夹下,这不就是用检测的json文件替代了之前的标注文件吗?不能这么操作啊!这种方法只能适用于检测图片没有标签文件的情况
  369. time_d[name] = os.path.getmtime(os.path.join(self.src_path, name)) # 修改时间
  370. # print("info: ", info) #python.exe C:/Users/ma/anaconda3/Lib/site-packages/labelme/__main__.py --labels ./labels.txt D:/data/data_original/20220516
  371. os.system(info)
  372. # 将在检测图片所在文件夹下的手工修正好的文件替换回去,代替之前的检测文件
  373. classes_dict = {"crack": 0, "hole": 1, "debonding": 2, "rarefaction": 3,
  374. "black_crack": 0, "black_hole": 1, "black_debonding": 2,
  375. "white_crack": 0, "white_hole": 1, "white_debonding": 2,
  376. "白色裂纹": 0, "白裂纹": 0, "裂纹": 0, "白色裂缝": 0, "白裂缝": 0, "裂缝": 0,
  377. "白色裂隙": 0, "白裂隙": 0, "裂隙": 0,
  378. "黑色裂纹": 0, "黑裂纹": 0, "黑色裂缝": 0, "黑裂缝": 0, "黑色裂隙": 0, "黑裂隙": 0,
  379. "白色孔洞": 1, "白孔洞": 1, "孔洞": 1, "黑色孔洞": 1, "黑孔洞": 1,
  380. "白色脱粘": 2, "白脱粘": 2, "脱粘": 2, "黑色脱粘": 2, "黑脱粘": 2,
  381. "疏松": 3} # 将各种叫法全合并为0、1、2、3四类
  382. json_list = glob.glob(self.src_path + '/*.json')
  383. for jsons in json_list:
  384. name = os.path.basename(jsons)
  385. changed = False
  386. # 旧json文件的修改时间
  387. if name not in time_d: # 运行这个if说明在图片文件夹下新增了json文件,这是用户纠错时添加的新json文件,内容是漏检的缺陷,需要拷贝回runs/detect文件夹下
  388. max_i += 1
  389. print("新增的json文件,复制到cor_path下:", os.path.join(self.cor_path, str(max_i) + ".json"))
  390. shutil.copyfile(jsons, os.path.join(self.det_path, name))
  391. shutil.move(jsons, os.path.join(self.cor_path, str(max_i) + ".json")) # 同时把新增的json文件命名为cor文件
  392. extenxion = ['.jpg', '.png', '.bmp', '.jpeg', '.tif', '.tiff']
  393. for ext in extenxion:
  394. imgf = (jsons[:-5] + ext)
  395. if os.path.exists(imgf):
  396. shutil.copyfile(imgf, os.path.join(self.cor_path, str(max_i) + ext))
  397. break
  398. continue
  399. old_t = time_d[name]
  400. new_t = os.path.getmtime(os.path.join(self.src_path, name))
  401. # new_t = os.path.getmtime(os.path.join(self.src_path, name))
  402. print("name, 时间差: ", name, new_t - old_t)
  403. print("修改时间相等吗:", (new_t - old_t) < 1)
  404. if (new_t - old_t) < 1: # 这个if成立了就说明新旧json文件的修改时间没变,删除src_path下的该json文件
  405. print("修改时间相等,删除:", os.path.join(self.src_path, name))
  406. os.remove(os.path.join(self.src_path, name))
  407. continue
  408. else: # 光修改时间变了不行,还要看里面的多边形是否变了
  409. old_masks = [[], [], [], []]
  410. new_masks = [[], [], [], []]
  411. # 找旧的masks
  412. with open(os.path.join(self.det_path, name)) as f:
  413. temp_shapes = json.loads(f.read())["shapes"]
  414. for old_polygon_temp in temp_shapes:
  415. old_masks[classes_dict[old_polygon_temp["label"]]].append(
  416. Polygon(old_polygon_temp["points"]))
  417. # 找新的masks
  418. with open(os.path.join(self.src_path, name)) as f:
  419. temp_shapes = json.loads(f.read())["shapes"]
  420. for new_polygon_temp in temp_shapes:
  421. new_masks[classes_dict[new_polygon_temp["label"]]].append(
  422. Polygon(new_polygon_temp["points"]))
  423. # print("src old_masks: ",name,old_masks)
  424. # print("out new_masks: ", name,new_masks)
  425. for i in range(len(old_masks)): # 首先看下新旧缺陷的每一类缺陷的数量是否完全相等,不相等就是改变了
  426. if len(old_masks[i]) != len(new_masks[i]):
  427. changed = True
  428. break
  429. print("缺陷数量比较结果 changed: ", changed)
  430. if changed == False: # 这个if成立了就说明新旧缺陷的数量完全相等,接下来就该比较新旧缺陷的iou了
  431. h, w = 5000, 5000
  432. for ext in ["jpg", "jpeg", "tif", "tiff", "png", "bmp"]:
  433. img_path = os.path.join(self.src_path, name[:-5] + "." + ext)
  434. # print("img_path: ",img_path)
  435. if os.path.exists(img_path):
  436. img = cv2.imread(img_path)
  437. h, w, _ = img.shape
  438. break
  439. # print("h,w: ",h,w)
  440. for i in range(len(old_masks)):
  441. for old_mask in old_masks[i]:
  442. match = False
  443. for new_mask in new_masks[i]:
  444. # old_area=old_mask.area
  445. # new_area=new_mask.area
  446. # 计算交集
  447. mask_gt = np.zeros([h, w, 3], dtype=np.int32) # 假定一个最大尺寸的空白图片,画真值框对应的区域
  448. ps = (list(old_mask.exterior.coords))
  449. ps = np.array(ps, dtype=np.int32)
  450. ps = ps.reshape(-1, 2)
  451. # f = open(dir + "/whole_" + task + "_" + str(i) + "_" + cls_names[i] + ".txt", "w")
  452. cv2.fillPoly(mask_gt, [ps], color=(125, 125, 125)) # 给真值框对应的空白图片按真值框的点集坐标上色
  453. gray = cv2.cvtColor(mask_gt.astype(np.float32), cv2.COLOR_BGR2GRAY)
  454. old_area = np.sum(gray == 125)
  455. # ret, thresh = cv2.threshold(gray, 125 - 1, 255, cv2.THRESH_BINARY)
  456. # contours, _ = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE,
  457. # cv2.CHAIN_APPROX_SIMPLE)
  458. #
  459. # old_area = 0
  460. # for c in contours: # 重叠区域可能有多个,把所有的面积加起来
  461. # area = cv2.contourArea(c, oriented=False)
  462. # old_area += area
  463. mask_pred = np.zeros([h, w, 3], dtype=np.int32)
  464. ps = (list(new_mask.exterior.coords))
  465. ps = np.array(ps, dtype=np.int32)
  466. ps = ps.reshape(-1, 2)
  467. # f = open(dir + "/whole_" + task + "_" + str(i) + "_" + cls_names[i] + ".txt", "w")
  468. cv2.fillPoly(mask_pred, [ps], color=(125, 125, 125))
  469. gray = cv2.cvtColor(mask_pred.astype(np.float32), cv2.COLOR_BGR2GRAY)
  470. new_area = np.sum(gray == 125)
  471. # ret, thresh = cv2.threshold(gray, 125 - 1, 255, cv2.THRESH_BINARY)
  472. # contours, _ = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE,
  473. # cv2.CHAIN_APPROX_SIMPLE)
  474. #
  475. # new_area = 0
  476. # for c in contours: # 重叠区域可能有多个,把所有的面积加起来
  477. # area = cv2.contourArea(c, oriented=False)
  478. # new_area += area
  479. gray = cv2.cvtColor((mask_pred + mask_gt).astype(np.float32),
  480. cv2.COLOR_BGR2GRAY) # 如果真值点集和预测点集有重叠区域,就对重叠区域计算轮廓面积
  481. inter_a = np.sum(gray == 125 * 2)
  482. # ret, thresh = cv2.threshold(gray, 250 - 1, 255, cv2.THRESH_BINARY)
  483. # contours, _ = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_TREE,
  484. # cv2.CHAIN_APPROX_SIMPLE)
  485. #
  486. # inter_a = 0
  487. # for c in contours: # 重叠区域可能有多个,把所有的面积加起来
  488. # area = cv2.contourArea(c, oriented=False)
  489. # inter_a += area
  490. iou = inter_a / (old_area + new_area - inter_a)
  491. # iou = self.poly_iou(old_mask, new_mask)
  492. # print("i, iou, old_area,new_area,inter_a,h,w: ", i, iou,old_area,new_area,inter_a,h,w)
  493. if iou > 0.95: # 如果old mask在new mask里能找到匹配,说明用户没有修old maks,就接着变量其他old mask
  494. match = True
  495. break
  496. if match == False: # 如果new mask遍历完了,还是没匹配上,说明old mask被用户改变了,此时changed置为True,并不再遍历其他old mas!
  497. changed = True
  498. break
  499. if changed == True: # 如果changed被置为True,就不再遍历其他类别的缺陷了,直接退出
  500. break
  501. if changed == True:
  502. max_i += 1
  503. print("json文件内容改变了,复制到cor_path下:", os.path.join(self.cor_path, str(max_i) + ".json"))
  504. shutil.copyfile(jsons, os.path.join(self.det_path, name))
  505. shutil.move(jsons, os.path.join(self.cor_path, str(max_i) + ".json"))
  506. extenxion = ['.jpg', '.png', '.bmp', '.jpeg', '.tif', '.tiff']
  507. for ext in extenxion:
  508. imgf = (jsons[:-5] + ext)
  509. if os.path.exists(imgf):
  510. shutil.copyfile(imgf, os.path.join(self.cor_path, str(max_i) + ext))
  511. break
  512. else:
  513. print("json文件没变,删除src_path下的json文件")
  514. os.remove(jsons)
  515. print("最终的changed: ", changed)
  516. print('close labelme!')
  517. except Exception as e:
  518. print('出错了:%s' % e)
  519. def poly_iou(self, poly1: Polygon, poly2: Polygon):
  520. intersection_area = poly1.intersection(poly2).area
  521. union_area = poly1.union(poly2).area
  522. return intersection_area / union_area
  523. class LoggerWindow():
  524. def __init__(self, mode='detect', path='') -> None:
  525. super().__init__()
  526. self.mode = mode # 训练还是检测日志窗口
  527. self.path = path # 读取日志文件
  528. self.ui = loadUi("qt_win/lx.ui") # 添加组件
  529. self.ui.label.setText(mode)
  530. # 读取文件
  531. f = open(path, 'r', encoding='utf-8')
  532. data = f.read()
  533. f.close()
  534. self.ui.textBrowser.append(data)
  535. class PlotThread(QThread): # 调用plots_new_one函数
  536. send_msg = pyqtSignal(str) # 信号
  537. def __init__(self):
  538. super(PlotThread, self).__init__()
  539. self.json_file = ""
  540. self.img_name = ""
  541. self.out_path = ""
  542. self.flag = False
  543. def run(self):
  544. try:
  545. self.send_msg.emit("开始显示结果")
  546. # 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)
  547. plots_new_one(self.json_file, self.img_name, self.out_path, self.flag)
  548. self.send_msg.emit("显示结果完成")
  549. except Exception as e:
  550. print('PlotThread Exception %s' % e)
  551. self.send_msg.emit("error")
  552. def write_xlsxwriter_new(sys, img_list, file_path, src_path, gsd):
  553. import xlsxwriter # 对excel数据进行写入的库
  554. # 新建工作簿
  555. writebook = xlsxwriter.Workbook(file_path) # 生成excel文件并设置编码为utf8
  556. sheet1 = writebook.add_worksheet() # 创建第一个sheet 表单
  557. # 注意:在 XlsxWriter 中,行和列都是零索引。第一个单元格 A1 是 (0, 0)。
  558. # 设置列宽
  559. sheet1.set_default_row(20)
  560. # sheet1.set_default_row(40)
  561. column_text_wrap = writebook.add_format()
  562. column_text_wrap.set_text_wrap()
  563. sheet1.set_column('A:A', 15)
  564. sheet1.set_column('B:B', 15)
  565. sheet1.set_column('E:E', 25, column_text_wrap)
  566. sheet1.set_column('F:F', 25, column_text_wrap)
  567. sheet1.set_column('K:K', 15)
  568. bold = writebook.add_format({
  569. 'bold': True, # 字体加粗
  570. 'border': 1, # 单元格边框宽度
  571. 'align': 'center',
  572. 'valign': 'vcentre', # 字体对齐方式
  573. 'text_wrap': True, # 是否自动换行
  574. })
  575. soft = writebook.add_format({
  576. 'bold': False, # 字体加粗
  577. 'border': 1, # 单元格边框宽度
  578. 'align': 'center',
  579. 'valign': 'vcentre', # 字体对齐方式
  580. 'text_wrap': True, # 是否自动换行
  581. })
  582. # title
  583. sheet1.merge_range(0, 0, 0, 16, sys, bold)
  584. sheet1.merge_range(1, 0, 1, 1, "图像名称", bold)
  585. sheet1.merge_range(1, 2, 1, 3, "缺陷类型", bold)
  586. sheet1.merge_range(1, 4, 1, 5, "缺陷位置(像素)", bold)
  587. sheet1.merge_range(1, 6, 1, 7, "缺陷面积(mm²)", bold)
  588. sheet1.merge_range(1, 8, 1, 9, "缺陷长度(mm)", bold)
  589. sheet1.merge_range(1, 10, 1, 11, "缺陷宽度(等效圆直径)(mm)", bold)
  590. sheet1.merge_range(1, 12, 1, 16, "检测图像", bold)
  591. # 加入内容
  592. start = 2
  593. print("img_list: ", len(img_list), img_list)
  594. for img_f in img_list:
  595. # 读取json文件内容,返回字典格式
  596. img_n = img_f.split('.')[-1]
  597. basename = os.path.basename(img_f)
  598. js = os.path.join(src_path, 'jsons', basename.replace(img_n, 'json'))
  599. if not os.path.exists(js):
  600. print("生成报表时 not exists json: ", js)
  601. continue
  602. with open(js, 'r', encoding='utf-8') as fp:
  603. json_data = json.load(fp)
  604. class_number = len(json_data['shapes']) # 类型个数
  605. # print("img_f, class_number: ", img_f, class_number)
  606. if class_number == 0:
  607. print("img_f, class_number==0: ", img_f, class_number)
  608. continue
  609. last = start + class_number - 1
  610. # first_row, first_col, last_row, last_col
  611. sheet1.merge_range(start, 0, last, 1, img_f, soft)
  612. neirong = json_data['shapes']
  613. # 加图 -- 判断是否存在 没有就重新画图
  614. # name = os.path.basename(img_f)
  615. if not os.path.exists(os.path.join(src_path, basename)):
  616. plots_new_one(js, img_f, os.path.join(src_path), False)
  617. img = cv2.imread(os.path.join(src_path, basename))
  618. h, w, _ = img.shape
  619. # row, col
  620. if class_number <= 1:
  621. nn = 1
  622. scale_x, scale_y = 300 / w, (25 * nn) / h
  623. else:
  624. nn = class_number
  625. scale_x, scale_y = 300 / w, (25 * nn) / h
  626. bsname, ext_old = basename.rsplit(".", 1)
  627. is_tif = False
  628. for ext in ["tif", "tiff"]:
  629. if ext_old == ext:
  630. new_basename = bsname + "_forxlsx.png"
  631. cv2.imwrite(os.path.join(src_path, new_basename), img)
  632. # shutil.copyfile(os.path.join(src_path, basename),os.path.join(src_path, new_basename))
  633. sheet1.insert_image(start, 12, os.path.join(src_path, new_basename),
  634. {'x_scale': scale_x, 'y_scale': scale_y, 'positioning': 1})
  635. print("insert success :", os.path.join(src_path, basename), os.path.join(src_path, new_basename))
  636. # os.remove(os.path.join(src_path, new_basename))
  637. is_tif = True
  638. break
  639. if is_tif == False:
  640. sheet1.insert_image(start, 12, os.path.join(src_path, basename),
  641. {'x_scale': scale_x, 'y_scale': scale_y, 'positioning': 1})
  642. for j in range(class_number):
  643. # 重新计算
  644. sheet1.merge_range(start + j, 2, start + j, 3, CT_name_zh[neirong[j]['label']], soft)
  645. segment = np.array(neirong[j]['points'], dtype=np.float32) # 获取每个轮廓点
  646. # print("segment,segment.shape: ",segment,segment.shape)
  647. # print("segment x: ",segment[:,0],np.mean(segment[:,0]))
  648. # print("segment y: ", segment[:, 1],np.mean(segment[:,1])
  649. if neirong[j]['label'] in ["hole", "rarefaction"]:
  650. area = round(cv2.contourArea(segment[:, None, :]) * gsd * gsd, 1)
  651. width = round(math.sqrt(4 * area / math.pi), 1)
  652. lenth = "-"
  653. else:
  654. area = "-"
  655. lenth = round(cv2.arcLength(segment[:, None:, ], True) / 2, 2)
  656. lenth = round(lenth * gsd)
  657. # 计算宽度
  658. width, _ = component_polygon_Circle_max(img, segment)
  659. width = round(width * gsd)
  660. cx, cy = np.around(np.mean(segment[:, 0]), 0), np.around(np.mean(segment[:, 1]), 0)
  661. cx, cy = int(cx), int(cy)
  662. sheet1.merge_range(start + j, 4, start + j, 5, "(" + str(cx) + "," + str(cy) + ")",
  663. soft) # neirong[j]['points']
  664. sheet1.merge_range(start + j, 6, start + j, 7, str(area), soft)
  665. sheet1.merge_range(start + j, 8, start + j, 9, str(lenth), soft)
  666. sheet1.merge_range(start + j, 10, start + j, 11, str(width), soft)
  667. start = last + 1
  668. # print("after img_list: ", len(img_list), img_list)
  669. writebook.close()
  670. for img_f in glob.glob(src_path + "/*_forxlsx.png"):
  671. os.remove(img_f)
  672. # print("remove success: ", img_f)
  673. class WriteThread(QThread):
  674. # write_xlsxwriter(msg,self.img_list,file_path)
  675. send_msg = pyqtSignal(str) # 信号
  676. def __init__(self):
  677. super(WriteThread, self).__init__()
  678. self.msg = ""
  679. self.imgs = []
  680. self.path = ""
  681. self.src = ""
  682. self.gsd = 0
  683. def run(self):
  684. try:
  685. self.send_msg.emit("开始导出报表")
  686. write_xlsxwriter_new(self.msg, self.imgs, self.path, self.src, self.gsd)
  687. self.send_msg.emit("导出报表完成")
  688. except Exception as e:
  689. print('WriteThread %s' % e)
  690. self.send_msg.emit("error")
  691. # 消息框 显示 进度
  692. class Window(QMainWindow, Ui_mainWindow):
  693. def __init__(self):
  694. super(Window, self).__init__()
  695. self.wel = Welcome()
  696. self.wel.login.show()
  697. self.wel.success.connect(self.new_init_)
  698. self.gsd_file = ""
  699. def new_init_(self):
  700. if self.wel.success:
  701. self.setupUi(self)
  702. ## 加载子窗口
  703. self.ui_detect = loadUi("qt_win/detect_par.ui") # 检测的参数选择
  704. self.ui_train = loadUi("qt_win/train_par.ui") # 训练时的参数选择
  705. # 训练子窗口 功能连接函数
  706. self.ui_train.pushButton_2.clicked.connect(self.SetTrainOK)
  707. self.ui_train.pushButton.clicked.connect(self.restore_set)
  708. self.ui_train.pushButton_3.clicked.connect(self.choose_train_file)
  709. self.ui_detect.pushButton_2.clicked.connect(self.SetDetectOK)
  710. self.ui_detect.pushButton.clicked.connect(self.restore_set)
  711. # flag
  712. self.m_flag = False # 鼠标事件标志
  713. self.t_flag = 0 # 训练标志,为False,所以在new_init_函数调用时默认模式是检测
  714. self.DC = "0"
  715. self.count_T = 0
  716. self.count_D = 0 # 当零的时候 即使用已存在预测结果画图
  717. # self.count_T_error = 0
  718. # self.count_D_error = 0
  719. # win10的CustomizeWindowHint模式,边框上面有一段空白
  720. self.setWindowFlags(Qt.FramelessWindowHint) # 去掉操作系统标题栏
  721. # 自定义标题栏按钮
  722. self.minButton.clicked.connect(self.showMinimized)
  723. self.maxButton.clicked.connect(self.max_or_restore)
  724. self.closeButton.clicked.connect(self.close)
  725. # 加拖动的功能
  726. # 定时清空自定义状态栏上的文字
  727. self.qtimer = QTimer(self)
  728. self.qtimer.setSingleShot(True)
  729. self.qtimer.timeout.connect(lambda: self.statistic_label.clear())
  730. # 训练和推理 功能选择按钮
  731. cb_group = QButtonGroup()
  732. cb_group.addButton(self.trainbutton, 0)
  733. cb_group.addButton(self.detectbutton, 1)
  734. cb_group.addButton(self.trainbutton_2, 2) # 新增功能
  735. cb_group.addButton(self.trainbutton_3, 3)
  736. cb_group.addButton(self.trainbutton_4, 4)
  737. cb_group.addButton(self.trainbutton_5, 5)
  738. cb_group.setExclusive(True) # 设置button互斥
  739. self.trainbutton.clicked.connect(self.chose_training)
  740. self.trainbutton_2.clicked.connect(self.chose_feature) # 新增功能
  741. self.trainbutton_3.clicked.connect(self.chose_jiance)
  742. self.trainbutton_4.clicked.connect(self.chose_q_jaince)
  743. self.trainbutton_5.clicked.connect(self.chose_gnn)
  744. self.detectbutton.clicked.connect(self.chose_detecting) # 主页面检测按钮的操函数,用于弹出检测参数设置对话框
  745. # 增加功能
  746. # 自动搜索模型 -- 增加 新训练 保存的模型
  747. self.model_path = './weights/' # 指定 保存模型路径
  748. self.feature_alg = './test_data/xie' # 新增功能
  749. self.comboBox.clear()
  750. pt_lists = os.listdir(self.model_path)
  751. py_lists = os.listdir(self.feature_alg) # 新增功能
  752. if self.t_flag == 1: # 特征提取模式
  753. py_lists = [file for file in py_lists if file.endswith('.py') and "feature" in file] # 新增功能
  754. if self.t_flag == 0: # 识别模式
  755. pt_lists = [file for file in pt_lists if file.endswith('.pt') and "yolov5" not in file]
  756. if self.t_flag == 2: # 训练模式
  757. pt_lists = [file for file in pt_lists if file.endswith('.pt') and "yolov5" in file]
  758. if self.t_flag == 3 or self.t_flag == 4: # 检测
  759. py_lists = [file for file in py_lists if file.endswith('.py') and "detection" in file]
  760. if self.t_flag == 4:
  761. pass
  762. if self.t_flag == 5:
  763. pass
  764. py_lists.sort(reverse=True) # 新增功能
  765. pt_lists.sort(reverse=True) # 为True是降序
  766. self.current_img = ''
  767. self.pt_lists = pt_lists
  768. self.py_lists = py_lists # 新增功能
  769. self.comboBox.addItems(self.pt_lists) # 列表中的每一项作为一个独立的选项加入到 comboBox 控件中。这使得用户可以从下拉菜单中选择一个项目。
  770. self.comboBox.addItems(self.py_lists) # 新增功能
  771. self.comboBox.currentTextChanged.connect(self.change_model) # 在comboBox 下拉菜单中选择一个不同的项目时,程序会自动调用 self.change_model 方法。
  772. # 自动搜索超参数文件
  773. self.hyps_path = './data/hyps/' # 指定 超参数路径
  774. self.ui_train.comboBox.clear()
  775. self.hyp_list = os.listdir(self.hyps_path)
  776. # print("self.hpy_list: ",self.hyp_list)
  777. self.hyp_list = [file for file in self.hyp_list if file.endswith('-low.yaml')]
  778. # print("after self.hpy_list: ",self.hyp_list)
  779. self.hyp_list.sort(key=lambda x: os.path.getsize(self.hyps_path + x))
  780. self.ui_train.comboBox.addItems(self.hyp_list)
  781. self.restore_set()
  782. self.Base_Path_D = "./runs/detect/base"
  783. Path(self.Base_Path_D).mkdir(parents=True, exist_ok=True) # 缓存json
  784. dtime = (QDateTime.currentDateTime().toString(Qt.ISODate)).split("T")[0]
  785. self.time = QDateTime.currentDateTime().toString(Qt.ISODate)
  786. self.runs_path = f"./runs/{self.wel.account}/{dtime}/detect" # 保存推理的路径 可以结合软件运行的时间
  787. if os.path.exists(self.runs_path):
  788. shutil.rmtree(self.runs_path) # os.rmdir(self.runs_path)
  789. if os.path.exists(self.runs_path.replace('detect', 'train')):
  790. shutil.rmtree(self.runs_path.replace('detect', 'train')) # os.rmdir(self.runs_path)
  791. # self.runs_path = "./runs/detect" #保存推理的路径 可以结合软件运行的时间
  792. # 系统信息框 -- 返回训练或者推理的过程信息 以及最终结果的统计信息等
  793. # 后续包装返回信息 #实例化列表模型,添加数据
  794. # self.listView.addItem(self.time + "\t软件执行信息") # slm = QStringListModel()
  795. # self.listView.itemDoubleClicked.connect(self.listViewdoubleClicked)
  796. # self.listView.itemClicked.connect(self.listViewClicked)
  797. # 读取 文件夹或文件中的图片
  798. self.img_list = [] # [r'images\one.jpg',r'images\two.jpg',r'images\three.jpg',r'images\four.jpg'] #存放图片文件名 -- 绝对路径
  799. self.output_file_list = {}
  800. self.index = 0
  801. self.choose_file.clicked.connect(self.open_file)
  802. self.choose_folder.clicked.connect(self.open_folder)
  803. self.resultWidget.itemClicked.connect(self.WidgetClicked)
  804. # # 选择圆盘
  805. # self.choose_circle.clicked.connect(self.get_choose_circle)
  806. # self.gsd.textChanged.connect(lambda current_text: self.gsd_textChanged_func(current_text)) # 槽函数
  807. # 显示图片
  808. self.pre_page.clicked.connect(self.change_image_up) # 上一页
  809. self.next_page.clicked.connect(self.change_image_down) # 下一页
  810. self.show_result.clicked.connect(self.show_output) # 显示结果
  811. # self.show_result.clicked.connect(self.show_res) # 显示结果
  812. ### 结果导出##
  813. self.result_export.clicked.connect(self.save_output) # save
  814. # 1.Qt 开一个 yolov5 线程 #用于模型推理
  815. self.det_thread = DetThread() # 增加一些槽 来获取返回信息
  816. self.det_thread.send_msg.connect(lambda x: self.show_msg(x))
  817. # 修正命令行内容
  818. # 1.Qt 开一个 yolov5 线程 #用于模型训练
  819. self.train_thread = TrainThread()
  820. self.train_thread.send_msg.connect(lambda x: self.show_msg2(x))
  821. self.fea_thread = FeaThread()
  822. self.fea_thread.send_msg.connect(lambda x: self.show_msg3(x))
  823. self.jian_thread = JianThread()
  824. self.jian_thread.send_msg.connect(lambda x: self.show_msg2(x))
  825. # 2. os.system 后台跑结果出来
  826. # 2. os.system 后台跑结果出来
  827. self.action.clicked.connect(self.begin_action) # 开始推理
  828. self.plots_thread = PlotThread() # 画图子进程
  829. self.plots_thread.send_msg.connect(lambda x: self.show_output_new(x)) # 将信号与槽函数连接起来
  830. self.plots_flag = False
  831. # self.close_info.clicked.connect(self.change_flag)
  832. self.write_thread = WriteThread() # 写excel子进程
  833. self.write_thread.send_msg.connect(lambda x: self.write_xlsxwriter(x))
  834. ###### TODO: 纠错 ####
  835. self.labelme_thread = LabelThread()
  836. self.error_correction.clicked.connect(self.open_labelme) # 纠错
  837. self.SetDetectOK()
  838. self.show()
  839. def gsd_textChanged_func(self, current_text):
  840. print("文本框内容变化信号", current_text)
  841. # self.textBrowser.append("文本框内容变化信号" + current_text + '\n')
  842. lines = []
  843. if self.shebei.text() != "" and self.beicepin.text() != "" and self.pici.text() != "" and current_text != "":
  844. lines.append(" ".join([self.shebei.text(), self.beicepin.text(), self.pici.text(), current_text]))
  845. print("lines: ", lines)
  846. with open(self.gsd_file, "w", encoding="utf-8") as f:
  847. for line in lines:
  848. f.writelines(line + "\n")
  849. self.out_video.gsd = float(current_text) # 画图类的gsd属
  850. self.gsd.setText(str(float(current_text)))
  851. # 选择圆盘
  852. def get_choose_circle(self):
  853. dis = math.sqrt(math.pow(self.raw_video.circle_p1.x() - self.raw_video.circle_p2.x(), 2) + math.pow(
  854. self.raw_video.circle_p1.y() - self.raw_video.circle_p2.y(), 2))
  855. print("dis: ", dis)
  856. gsd = 0
  857. if self.zhijing.text() != "" and dis > 0:
  858. print("self.zhijing.text()")
  859. gsd = round(int(self.zhijing.text()) / dis, 2)
  860. print("gsd: ", gsd)
  861. print("self.gsd.setText before :", gsd)
  862. self.gsd.setText(str(gsd))
  863. print("self.gsd.setText after :", gsd)
  864. ############后台信息显示 -- 方便调试##################
  865. def statistic_msg(self, msg, time=-1):
  866. self.statistic_label.setText(msg)
  867. self.qtimer.start(time) # 3秒后自动清除
  868. ######################子窗口部分###############################
  869. # 恢复默认参数
  870. def restore_set(self):
  871. # if not self.trainbutton.isEnabled():
  872. self.statistic_msg("恢复默认参数", -1) # 恢复默认 可以不用管 直接把
  873. self.ui_train.radioButton_2.setChecked(True) #
  874. self.ui_train.radioButton_2.setEnabled(False) # 预训练
  875. self.ui_train.lineEdit_2.setText("200") # epochs
  876. self.ui_train.lineEdit_3.setText("32") # batch size
  877. self.ui_train.lineEdit_4.setText("1e-2") # lr
  878. self.ui_train.lineEdit_5.setText("640") # img size
  879. # 设置用户训练的模型的名称
  880. # {self.wel.account} / {dtime} / detect
  881. # dtime = (QDateTime.currentDateTime().toString(Qt.ISODate)).split("T")[0]
  882. # self.ui_train.lineEdit_6.setText(f"{dtime}_{self.wel.account}" + '.pt') # ckpt name
  883. current_datetime = datetime.now()
  884. # 格式化日期和时间,例如:"2024-06-11 15:30:45"
  885. formatted_datetime = current_datetime.strftime(f"%Y-%m-%d_%H%M" + ".pt")
  886. self.ui_train.lineEdit_6.setText(formatted_datetime) # ckpt name
  887. self.ui_train.radioButton_3.setChecked(False) # 是否使用图像列表数据加入样本进行再训练
  888. self.ui_train.radioButton_3.setEnabled(False) # 设置单选按钮为不可选状态
  889. self.radio_layout1 = QButtonGroup(self)
  890. self.radio_layout2 = QButtonGroup(self)
  891. self.radio_layout1.addButton(self.ui_train.radioButton_2)
  892. self.radio_layout2.addButton(self.ui_train.radioButton_3)
  893. # else:
  894. # self.statistic_msg("恢复默认检测参数") #恢复默认 可以不用管 直接把
  895. self.ui_detect.lineEdit.setText("0.5")
  896. self.ui_detect.lineEdit_2.setText("0.4")
  897. self.ui_detect.lineEdit_3.setText("256")
  898. self.ui_detect.radioButton.setChecked(True) # 是否使用图像切片预测
  899. self.ui_detect.radioButton.setEnabled(False) # 设置单选按钮为不可选状态,即只支持切片预测
  900. self.ui_detect.lineEdit_4.setText("640") # 判断元组还数字
  901. ## 子窗口弹出
  902. def chose_training(self):
  903. self.statistic_msg("选择训练功能,请设置超参数", -1)
  904. self.ui_detect.close()
  905. self.ui_train.show()
  906. self.t_flag = 2
  907. self.search_pt()
  908. if self.trainbutton.isEnabled(): # 增加flag
  909. self.trainbutton.setEnabled(False)
  910. self.trainbutton_4.setEnabled(True)
  911. self.trainbutton_5.setEnabled(True)
  912. self.detectbutton.setEnabled(True)
  913. self.trainbutton_2.setEnabled(True)
  914. self.trainbutton_3.setEnabled(True)
  915. else:
  916. self.trainbutton.setEnabled(True)
  917. self.trainbutton_4.setEnabled(False)
  918. self.trainbutton_5.setEnabled(False)
  919. self.detectbutton.setEnabled(False)
  920. self.trainbutton_2.setEnabled(False)
  921. self.trainbutton_3.setEnabled(False)
  922. def chose_detecting(self):
  923. self.statistic_msg("选择推理功能,请设置超参数", -1)
  924. self.t_flag = 0
  925. self.search_pt()
  926. self.ui_detect.show()
  927. self.ui_train.close()
  928. if self.detectbutton.isEnabled(): # 增加flag
  929. self.trainbutton.setEnabled(True)
  930. self.trainbutton_4.setEnabled(True)
  931. self.trainbutton_5.setEnabled(True)
  932. self.detectbutton.setEnabled(False)
  933. self.trainbutton_2.setEnabled(True)
  934. self.trainbutton_3.setEnabled(True)
  935. else:
  936. self.detectbutton.setEnabled(True)
  937. self.trainbutton_4.setEnabled(False)
  938. self.trainbutton_5.setEnabled(False)
  939. self.trainbutton.setEnabled(False)
  940. self.trainbutton_2.setEnabled(False)
  941. self.trainbutton_3.setEnabled(False)
  942. def chose_feature(self):
  943. self.t_flag = 1
  944. self.search_py()
  945. self.ui_detect.close()
  946. self.ui_train.close()
  947. if self.trainbutton_2.isEnabled(): # 增加flag
  948. self.trainbutton_2.setEnabled(False)
  949. self.trainbutton_4.setEnabled(True)
  950. self.trainbutton_5.setEnabled(True)
  951. self.detectbutton.setEnabled(True)
  952. self.trainbutton.setEnabled(True)
  953. self.trainbutton_3.setEnabled(True)
  954. else:
  955. self.trainbutton_2.setEnabled(True)
  956. self.trainbutton_4.setEnabled(False)
  957. self.trainbutton_5.setEnabled(False)
  958. self.detectbutton.setEnabled(False)
  959. self.trainbutton.setEnabled(False)
  960. self.trainbutton_3.setEnabled(False)
  961. def chose_jiance(self):
  962. self.t_flag = 3
  963. self.search_py()
  964. self.ui_detect.close()
  965. self.ui_train.close()
  966. if self.trainbutton_3.isEnabled(): # 增加flag
  967. self.trainbutton_3.setEnabled(False)
  968. self.trainbutton_4.setEnabled(True)
  969. self.trainbutton_5.setEnabled(True)
  970. self.trainbutton_2.setEnabled(True)
  971. self.detectbutton.setEnabled(True)
  972. self.trainbutton.setEnabled(True)
  973. else:
  974. self.trainbutton_3.setEnabled(True)
  975. self.trainbutton_4.setEnabled(False)
  976. self.trainbutton_5.setEnabled(False)
  977. self.trainbutton_2.setEnabled(False)
  978. self.detectbutton.setEnabled(False)
  979. self.trainbutton.setEnabled(False)
  980. def chose_q_jaince(self):
  981. self.t_flag = 4
  982. self.search_py()
  983. self.ui_detect.close()
  984. self.ui_train.close()
  985. if self.trainbutton_4.isEnabled(): # 增加flag
  986. self.trainbutton_4.setEnabled(False)
  987. self.trainbutton_5.setEnabled(True)
  988. self.trainbutton_3.setEnabled(True)
  989. self.trainbutton_2.setEnabled(True)
  990. self.detectbutton.setEnabled(True)
  991. self.trainbutton.setEnabled(True)
  992. else:
  993. self.trainbutton_4.setEnabled(True)
  994. self.trainbutton_5.setEnabled(False)
  995. self.trainbutton_3.setEnabled(False)
  996. self.trainbutton_2.setEnabled(False)
  997. self.detectbutton.setEnabled(False)
  998. self.trainbutton.setEnabled(False)
  999. def chose_gnn(self):
  1000. self.t_flag = 5
  1001. self.search_py()
  1002. self.ui_detect.close()
  1003. self.ui_train.close()
  1004. if self.trainbutton_5.isEnabled(): # 增加flag
  1005. self.trainbutton_4.setEnabled(True)
  1006. self.trainbutton_5.setEnabled(False)
  1007. self.trainbutton_3.setEnabled(True)
  1008. self.trainbutton_2.setEnabled(True)
  1009. self.detectbutton.setEnabled(True)
  1010. self.trainbutton.setEnabled(True)
  1011. else:
  1012. self.trainbutton_4.setEnabled(False)
  1013. self.trainbutton_5.setEnabled(True)
  1014. self.trainbutton_3.setEnabled(False)
  1015. self.trainbutton_2.setEnabled(False)
  1016. self.detectbutton.setEnabled(False)
  1017. self.trainbutton.setEnabled(False)
  1018. def choose_train_file(self):
  1019. print("choose_file")
  1020. # 多选对话框
  1021. file_dialog = QFileDialog()
  1022. file_dialog.setFileMode(QFileDialog.DirectoryOnly)
  1023. file_dialog.setOption(QFileDialog.DontUseNativeDialog, True)
  1024. file_dialog.setDirectory('d:/data')
  1025. file_view = file_dialog.findChild(QListView, 'listView')
  1026. if file_view:
  1027. file_view.setSelectionMode(QAbstractItemView.MultiSelection)
  1028. f_tree_view = file_dialog.findChild(QTreeView)
  1029. if f_tree_view:
  1030. f_tree_view.setSelectionMode(QAbstractItemView.MultiSelection)
  1031. if file_dialog.exec_():
  1032. folder = file_dialog.selectedFiles()
  1033. print("folder: ", folder)
  1034. line = ""
  1035. for f in folder:
  1036. line += f + ";"
  1037. self.ui_train.lineEdit_7.setText(line)
  1038. def SetTrainOK(self):
  1039. self.statistic_msg("训练参数设置完毕", -1)
  1040. # 打开训练设置窗口 获取参数
  1041. # list 存放 #str 类型
  1042. self.train_thread.Command = [str(self.ui_train.radioButton_2.isChecked()), self.ui_train.lineEdit_5.text(),
  1043. self.ui_train.lineEdit_2.text(), \
  1044. self.ui_train.lineEdit_3.text(), self.ui_train.lineEdit_4.text(), \
  1045. self.model_path + self.ui_train.lineEdit_6.text(),
  1046. self.hyps_path + self.ui_train.comboBox.currentText(),
  1047. self.runs_path.replace('detect', 'train'), \
  1048. self.ui_train.lineEdit_7.text()]
  1049. if self.ui_train.radioButton_2.isChecked():
  1050. self.traing_hyp = f" 启动{self.comboBox.currentText()}预训练 "
  1051. else:
  1052. self.traing_hyp = " 不启动预训练 "
  1053. 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()} " + \
  1054. f"lr:{self.ui_train.lineEdit_4.text()} " + f"hyp:{self.ui_train.comboBox.currentText()}"
  1055. print("self.train_thread.Command,self.traing_hyp:", self.train_thread.Command, self.traing_hyp)
  1056. # 关闭窗口
  1057. self.ui_train.close()
  1058. def SetDetectOK(self):
  1059. self.statistic_msg("检测参数设置完毕", -1)
  1060. # 打开检测设置窗口 获取参数
  1061. # list 存放 #str 类型
  1062. self.det_thread.Command = [self.ui_detect.lineEdit_4.text(), self.ui_detect.lineEdit.text(),
  1063. self.ui_detect.lineEdit_2.text(), self.ui_detect.lineEdit_3.text(), self.runs_path]
  1064. if self.ui_detect.radioButton.isChecked():
  1065. self.det_thread.split = True
  1066. self.detect_hyp = f" 启动重叠{self.ui_detect.lineEdit_3.text()}的切片预测 "
  1067. else:
  1068. self.det_thread.split = False
  1069. self.detect_hyp = " 不启动切片预测 "
  1070. 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()}"
  1071. # 关闭窗口
  1072. if self.ui_detect.isVisible() == True: # 我加的,如果窗口没有打开,就不用关闭了
  1073. self.ui_detect.close()
  1074. # print("self.det_thread.Command,split", self.det_thread.Command, self.det_thread.split)
  1075. ###########设置区---- 模型选择部分#############
  1076. # 模型选择
  1077. def change_model(self, x):
  1078. self.model_type = self.comboBox.currentText()
  1079. self.statistic_msg('模型切换为%s' % x, -1)
  1080. # 规定存放模型路径 -- update 模型名字
  1081. def search_pt(self):
  1082. pt_lists = os.listdir(self.model_path) #
  1083. print("search_pt pt_lists: ", pt_lists)
  1084. if self.t_flag == 0: # 检测模式
  1085. pt_lists = [file for file in pt_lists if file.endswith('.pt') and "yolov5" not in file]
  1086. elif self.t_flag == 2:
  1087. pt_lists = [file for file in pt_lists if file.endswith('.pt') and "yolov5" in file]
  1088. print("after search_pt pt_lists: ", self.t_flag, pt_lists)
  1089. pt_lists.sort(reverse=True)
  1090. # if pt_lists != self.pt_lists:
  1091. self.pt_lists = pt_lists
  1092. self.comboBox.clear()
  1093. self.comboBox.addItems(self.pt_lists)
  1094. def search_py(self):
  1095. py_lists = os.listdir(self.feature_alg) #
  1096. print("search_py py_lists: ", py_lists)
  1097. if self.t_flag == 1: # 特征提取模式
  1098. py_lists = [file for file in py_lists if file.endswith('.py') and "feature" in file]
  1099. elif self.t_flag == 3 or self.t_flag == 4: # 检测模式
  1100. py_lists = [file for file in py_lists if file.endswith('.py') and 'detection' in file]
  1101. print("after search_pt pt_lists: ", self.t_flag, py_lists)
  1102. py_lists.sort(reverse=True)
  1103. # if pt_lists != self.pt_lists:
  1104. self.py_lists = py_lists
  1105. self.comboBox.clear()
  1106. self.comboBox.addItems(self.py_lists)
  1107. def show_msg(self, msg):
  1108. if "error" in msg.lower():
  1109. self.statistic_msg(msg, -1)
  1110. MessageBox(self.closeButton, title='提示', text='检测过程出现错误,请查看相关日志文件', time=1000,
  1111. auto=True).exec_()
  1112. # self.count_D_error += 1
  1113. elif msg == "开始检测":
  1114. self.statistic_msg(msg, -1)
  1115. self.tips = MessageBox(
  1116. self.closeButton, title='提示', text=f'请稍等,正在进行第{self.count_D}次检测', time=500,
  1117. auto=False) # .exec_()
  1118. self.tips.show()
  1119. elif msg == "检测结束":
  1120. self.statistic_msg(msg, -1)
  1121. self.tips.close()
  1122. MessageBox(
  1123. self.closeButton, title='提示', text=f'第{self.count_D}次检测结束,正在关闭提示窗口', time=1000,
  1124. auto=True).exec_()
  1125. # self.tips.show()
  1126. # self.listView.addItem(QDateTime.currentDateTime().toString(Qt.ISODate) + f"\t第{self.count_D}次检测结束,超参数:" + self.detect_hyp) #双击查看日志
  1127. # self.statistic_label.clear()
  1128. def show_msg2(self, msg):
  1129. if "error" in msg.lower():
  1130. self.statistic_msg(msg, -1)
  1131. MessageBox(
  1132. self.closeButton, title='提示', text='训练过程出现错误,请查看相关日志文件', time=1000,
  1133. auto=True).exec_()
  1134. # 这里 因为训练错误 来一个计算失败的
  1135. # count_T_error += 1
  1136. elif msg == "开始训练":
  1137. self.statistic_msg(msg, -1)
  1138. MessageBox(self.closeButton, title='提示', text=f'请稍等,正在进行第{self.count_T}次训练', time=5000,
  1139. auto=True).exec_()
  1140. # QMessageBox.information(self,"提示", f'请稍等,正在进行第{self.count_T}次训练。。。')
  1141. # InfoMessageBox.information(self, "提示", f'请稍等,正在进行第{self.count_T}次训练。。。')
  1142. # self.tips.show()
  1143. self.statistic_msg("正在训练中,请不要操作", -1)
  1144. elif msg == "训练结束":
  1145. # self.train_tips.close()
  1146. self.statistic_msg(msg, -1)
  1147. MessageBox(
  1148. self.closeButton, title='提示', text=f'第{self.count_T}次训练结束,正在关闭提示窗口。。。', time=500,
  1149. auto=True).exec_()
  1150. # self.statistic_label.clear()
  1151. # self.listView.addItem(QDateTime.currentDateTime().toString(Qt.ISODate) + f"\t第{self.count_T}次训练j结束,超参数:" + self.traing_hyp)
  1152. # self.search_pt()
  1153. def show_msg3(self, msg):
  1154. if "error" in msg.lower():
  1155. self.statistic_msg(msg, -1)
  1156. MessageBox(self.closeButton, title='提示', text='特征提取过程出现错误,请查看相关日志文件', time=1000,
  1157. auto=True).exec_()
  1158. # self.count_D_error += 1
  1159. elif msg == "开始特征提取":
  1160. self.statistic_msg(msg, -1)
  1161. self.tips = MessageBox(
  1162. self.closeButton, title='提示', text=f'请稍等,正在进行第{self.count_D}次提取。。。', time=500,
  1163. auto=False) # .exec_()
  1164. self.tips.show()
  1165. elif msg == "特征提取结束":
  1166. self.statistic_msg(msg, -1)
  1167. self.tips.close()
  1168. MessageBox(
  1169. self.closeButton, title='提示', text=f'第{self.count_D}次检测结束,正在关闭提示窗口。。。', time=1000,
  1170. auto=True).exec_()
  1171. ################# 自定义标题栏按钮--功能##########################
  1172. def max_or_restore(self):
  1173. if self.maxButton.isChecked():
  1174. self.showMaximized()
  1175. else:
  1176. self.showNormal()
  1177. def closeEvent(self, event):
  1178. MessageBox(
  1179. self.closeButton, title='提示', text='请稍等,正在关闭程序。。。', time=500, auto=True).exec_()
  1180. # #删除文件 来kill进程
  1181. # if self.count_T == 1:
  1182. # if os.path.exists(os.path.join(self.runs_path.replace('detect','train'),f'exp{self.count_T}')):
  1183. # shutil.rmtree(os.path.join(self.runs_path.replace('detect','train'),f'exp{self.count_T}')) #os.rmdir(self.runs_path)
  1184. # else:
  1185. # if os.path.exists(os.path.join(self.runs_path.replace('detect','train'),f'exp')):
  1186. # shutil.rmtree(os.path.join(self.runs_path.replace('detect','train'),f'exp')) #os.rmdir(self.runs_path)
  1187. self.train_thread.exit() # 没用
  1188. self.det_thread.exit()
  1189. # self.train_thread.exit()
  1190. kill_name("python.exe")
  1191. sys.exit(0)
  1192. ## 鼠标获取
  1193. def mousePressEvent(self, event):
  1194. self.m_Position = event.pos()
  1195. if event.button() == Qt.LeftButton:
  1196. if 0 < self.m_Position.x() < self.groupBox.pos().x() + self.groupBox.width() and \
  1197. 0 < self.m_Position.y() < self.groupBox.pos().y() + self.groupBox.height():
  1198. self.m_flag = True
  1199. def mouseMoveEvent(self, QMouseEvent):
  1200. if Qt.LeftButton and self.m_flag:
  1201. self.move(QMouseEvent.globalPos() - self.m_Position) # 更改窗口位置
  1202. # QMouseEvent.accept()
  1203. def mouseReleaseEvent(self, QMouseEvent):
  1204. self.m_flag = False
  1205. # self.setCursor(QCursor(Qt.ArrowCursor))
  1206. ######################可视化图片####################
  1207. # 上一页
  1208. def change_image_up(self):
  1209. try:
  1210. self.index = self.index - 1
  1211. if self.index <= 0:
  1212. self.index = 0
  1213. if len(self.img_list) > 0:
  1214. img_file = self.img_list[self.index] # 图像列表
  1215. self.current_img = img_file
  1216. # img = cv2.imread(img_file)
  1217. self.statistic_msg(img_file, -1) #
  1218. self.show_image(img_file, self.raw_video)
  1219. self.label_6.setText(f"{os.path.basename(img_file)}原图像")
  1220. currRow = self.resultWidget.currentRow()
  1221. if currRow > 0:
  1222. self.resultWidget.setCurrentRow(currRow - 1)
  1223. except Exception as e:
  1224. self.statistic_msg(repr(e), -1)
  1225. # 下一页
  1226. def change_image_down(self):
  1227. try:
  1228. self.index = self.index + 1 #
  1229. if len(self.img_list) > 0:
  1230. if self.index >= len(self.img_list) - 1:
  1231. self.index = len(self.img_list) - 1
  1232. img_file = self.img_list[self.index] # 图像列表
  1233. self.current_img = img_file
  1234. self.statistic_msg(img_file) #
  1235. self.show_image(img_file, self.raw_video)
  1236. self.label_6.setText(f"{os.path.basename(img_file)}原图像")
  1237. currRow = self.resultWidget.currentRow()
  1238. rowAll = self.resultWidget.count()
  1239. if currRow < rowAll - 1:
  1240. self.resultWidget.setCurrentRow(currRow + 1)
  1241. except Exception as e:
  1242. self.statistic_msg(repr(e))
  1243. # def change_flag(self):
  1244. # 显示结果
  1245. def show_res(self):
  1246. try:
  1247. if len(self.img_list) > 0:
  1248. if self.index >= len(self.img_list) - 1:
  1249. self.index = len(self.img_list) - 1
  1250. img_file = self.img_list[self.index] # 图像列表
  1251. self.current_img = img_file
  1252. self.statistic_msg(img_file) #
  1253. self.show_image(img_file, self.raw_video)
  1254. self.label_6.setText(f"{os.path.basename(img_file)}原图像")
  1255. currRow = self.resultWidget.currentRow()
  1256. rowAll = self.resultWidget.count()
  1257. if currRow < rowAll - 1:
  1258. self.resultWidget.setCurrentRow(currRow + 1)
  1259. except Exception as e:
  1260. self.statistic_msg(repr(e))
  1261. # def change_flag(self):
  1262. # self.plots_flag = not self.plots_flag #取反
  1263. # if self.plots_flag:#显示信息
  1264. # self.close_info.setText("关闭信息显示")
  1265. # else:
  1266. # self.close_info.setText("打开信息显示")
  1267. # 输出显示
  1268. def show_output(self):
  1269. try:
  1270. if self.DC != "0":
  1271. self.out_video.clear()
  1272. if self.t_flag == 1 or self.t_flag == 3 or self.t_flag == 4:
  1273. curr_path = self.output_file_list[self.img_list[self.index]]
  1274. self.plots_thread.img_name = curr_path
  1275. self.plots_thread.out_path = curr_path
  1276. self.plots_thread.flag = self.plots_flag
  1277. self.plots_thread.start() # 开启
  1278. # elif self.t_flag == 0:
  1279. # curr_path = "D:\\hiddz\\SAR\\runs\\asd\\2024-06-14\\detect\\exp\\HB14932.JPG"
  1280. # self.plots_thread.img_name = curr_path
  1281. # self.plots_thread.out_path = curr_path
  1282. # self.plots_thread.flag = self.plots_flag
  1283. # self.plots_thread.start()
  1284. else:
  1285. s = f'exp' if self.DC == "1" else f'exp{self.DC}'
  1286. ext = os.path.basename(self.img_list[self.index]).split('.')[-1] # 后缀名
  1287. # print("show_output: ", os.path.basename(self.img_list[self.index]) + f"第{self.DC}次检测结果") #201312040003B.jpeg第1次检测结果
  1288. self.statistic_msg(os.path.basename(self.img_list[self.index]) + f"第{self.DC}次检测结果")
  1289. # self.plots_thread.json_file = os.path.join(self.runs_path, s, "jsons",
  1290. # os.path.basename(self.img_list[self.index]).replace(ext,
  1291. # 'json'))
  1292. img_name = self.plots_thread.img_name = self.img_list[self.index].split('\\')[1]
  1293. path = os.path.join(self.runs_path, s, img_name)
  1294. # path = "D:\\hiddz\\SAR\\runs\\asd\\2024-06-14\\detect\\exp\\HB14932.JPG"
  1295. # path =
  1296. # path = self.plots_thread.out_path
  1297. self.plots_thread.img_name = path
  1298. self.plots_thread.out_path = path
  1299. self.plots_thread.flag = self.plots_flag
  1300. self.plots_thread.start() # 开启
  1301. else:
  1302. self.statistic_msg("目前没有检测结果,需要先进行检测......") # 我将下面的代码全部注释掉了,即:
  1303. # # 使用默认的 label
  1304. # #dirs = os.path.dirname(self.img_list[self.index])
  1305. # ext = os.path.basename(self.img_list[self.index]).split('.')[-1] #后缀名
  1306. # if os.path.exists(self.img_list[self.index].replace(ext,'json')):
  1307. # self.statistic_msg("建议首先选择一个模型进行检测,这里使用默认的检测结果")
  1308. # self.plots_thread.json_file = self.img_list[self.index].replace(ext,'json') #os.path.join(dirs,name.split(name,'json'))
  1309. # self.plots_thread.img_name = self.img_list[self.index]
  1310. # self.plots_thread.out_path = self.Base_Path_D
  1311. # self.plots_thread.flag = self.plots_flag
  1312. # self.plots_thread.start() #开启
  1313. # else:
  1314. # self.statistic_msg("未存在默认的检测结果,请首先选择一个模型进行检测")
  1315. except Exception as e:
  1316. self.statistic_msg(repr(e))
  1317. print(repr(e))
  1318. def show_output_new(self, msg): # 子进程的消息
  1319. if msg == "error":
  1320. self.statistic_msg(msg)
  1321. self.tips.close()
  1322. MessageBox(
  1323. self.closeButton, title='提示', text='模型未检测到结果,可视化原图', time=1000, auto=True).exec_()
  1324. self.show_image(self.plots_thread.img_name, self.out_video)
  1325. name = os.path.basename(self.plots_thread.img_name)
  1326. if self.DC != "0":
  1327. self.label_9.setText(f"{name}第{self.DC}次检测结果")
  1328. else:
  1329. self.label_9.setText("出错了,目前还没有检测过") # 我改的
  1330. # self.label_9.setText(f"{os.path.basename(self.img_list[self.index])}默认检测结果")
  1331. elif msg == "开始显示结果":
  1332. self.statistic_msg(msg)
  1333. self.tips = MessageBox(
  1334. self.closeButton, title='提示', text=f'请稍等,正在可视化检测结果。。。', time=500, auto=False) # $.exec_()
  1335. self.tips.show()
  1336. elif msg == "显示结果完成":
  1337. self.statistic_msg(msg)
  1338. self.tips.close()
  1339. MessageBox(
  1340. self.closeButton, title='提示', text=f'可视化检测结果结束,正在关闭提示窗口。。。', time=1000,
  1341. auto=True).exec_()
  1342. if self.t_flag == 1 or self.t_flag == 3 or self.t_flag == 4:
  1343. name = os.path.basename(self.plots_thread.img_name)
  1344. self.show_image(self.plots_thread.out_path, self.out_video)
  1345. else:
  1346. name = os.path.basename(self.plots_thread.img_name)
  1347. # self.show_image(os.path.join(self.plots_thread.out_path, name), self.out_video,
  1348. # json_file=self.plots_thread.json_file)
  1349. self.show_image(self.plots_thread.out_path, self.out_video)
  1350. # global det_img
  1351. # self.show_image(det_img, self.out_video)
  1352. if self.DC != "0":
  1353. self.label_9.setText(f"{name}第{self.DC}次检测结果")
  1354. else:
  1355. self.label_9.setText("目前还没有检测过") # 我改的
  1356. # self.label_9.setText(f"{os.path.basename(self.img_list[self.index])}默认检测结果")
  1357. def show_image(self, img_src, label, json_file=""):
  1358. try:
  1359. print("show_image, img_src, label,json_file:", img_src, label, json_file)
  1360. label.set_image(img_src, json_file)
  1361. except Exception as e:
  1362. print(repr(e))
  1363. ######读取图像#######
  1364. def open_file(self):
  1365. cur_work_path = os.path.dirname(__file__)
  1366. cur_file_path = None
  1367. if os.path.exists(cur_work_path + "/last_open.txt"):
  1368. with open(cur_work_path + "/last_open.txt", "r") as dir_f:
  1369. lines = dir_f.readlines()
  1370. if len(lines) > 0:
  1371. cur_file_path = lines[0]
  1372. # print("cur_work_path cur_file_path: ", cur_work_path, cur_file_path)
  1373. # folder = QFileDialog.getExistingDirectory(self, '选择图像文件夹...', cur_path)
  1374. source = QFileDialog.getOpenFileName(self, '选取图片', cur_file_path if cur_file_path else cur_work_path,
  1375. "JPEG Files(*.jpg *.jpeg);;PNG Files(*.png);;BMP Files(*.bmp);;TIF Files(*.tif *.tiff)")
  1376. # print("source: ", source)
  1377. if source[0] != "":
  1378. # 每次更换文件夹 所有检测结果就清除掉
  1379. if os.path.exists(self.runs_path):
  1380. shutil.rmtree(self.runs_path) # os.rmdir(self.runs_path)
  1381. if os.path.exists(self.runs_path.replace('detect', 'train')):
  1382. shutil.rmtree(self.runs_path.replace('detect', 'train')) # os.rmdir(self.runs_path)
  1383. # 显示窗口清除
  1384. # self.listView.clear()
  1385. # self.listView.addItem(self.time + "\t软件执行信息")
  1386. # 计算器清零
  1387. self.count_D = 0
  1388. self.DC = str(self.count_D)
  1389. self.count_T = 0
  1390. self.img_list.clear()
  1391. self.img_list.append(source[0])
  1392. self.show_list()
  1393. with open(cur_work_path + "/last_open.txt", "w") as dir_f:
  1394. dir_f.writelines(source[0])
  1395. self.gsd_file = os.path.join(os.path.dirname(source[0]), "gsd.txt")
  1396. print("open_file gsd_file: ", self.gsd_file)
  1397. if os.path.exists(self.gsd_file):
  1398. with open(self.gsd_file, "r", encoding="utf-8") as f:
  1399. for line in f.readlines():
  1400. line = line.strip()
  1401. print("line: ", line)
  1402. if len(line.split(" ")) == 4:
  1403. shebei_, beicepin_, pici_, gsd_ = line.split(" ")
  1404. shebei = shebei_
  1405. beicepin = beicepin_
  1406. pici = pici_
  1407. gsd = gsd_
  1408. print("打开文件 找到雷达模式,数据集,目标种类相匹配的gsd:", shebei_, beicepin_, pici_, gsd_)
  1409. break
  1410. else:
  1411. pici = os.path.basename(os.path.dirname(source[0]))
  1412. print("pici: ", pici)
  1413. beicepin = os.path.basename(os.path.dirname(os.path.dirname(source[0])))
  1414. print("beicepin: ", beicepin)
  1415. shebei = os.path.basename(os.path.dirname(os.path.dirname(os.path.dirname(source[0]))))
  1416. print("shebei: ", shebei)
  1417. gsd = 0
  1418. print("over 0")
  1419. self.shebei.setText(str(shebei))
  1420. print("over 1")
  1421. self.beicepin.setText(str(beicepin))
  1422. print("over 2")
  1423. self.pici.setText(str(pici))
  1424. print("over 3")
  1425. # self.gsd.setText(str(gsd))
  1426. # print("over 4")
  1427. else:
  1428. self.statistic_msg("未选择图片,请重新选取图片路径")
  1429. # print("self.img_list,source: ", self.img_list,source)
  1430. def open_folder(self):
  1431. cur_path = os.path.dirname(__file__)
  1432. last_path = None
  1433. if os.path.exists(cur_path + "/last_open_dir.txt"):
  1434. with open(cur_path + "/last_open_dir.txt", "r") as dir_f:
  1435. lines = dir_f.readlines()
  1436. if len(lines) > 0:
  1437. last_path = lines[0]
  1438. # print("cur_path: ", cur_path)
  1439. folder = QFileDialog.getExistingDirectory(self, '选择图像文件夹...', last_path if last_path else cur_path)
  1440. if folder != "":
  1441. # 每次更换文件夹 所有检测结果就清除掉
  1442. if os.path.exists(self.runs_path):
  1443. shutil.rmtree(self.runs_path) # os.rmdir(self.runs_path)
  1444. if os.path.exists(self.runs_path.replace('detect', 'train')):
  1445. shutil.rmtree(self.runs_path.replace('detect', 'train')) # os.rmdir(self.runs_path)
  1446. # 显示窗口清除
  1447. # self.listView.clear()
  1448. # self.listView.addItem(self.time + "\t软件执行信息")
  1449. # 计算器清零
  1450. self.count_D = 0
  1451. self.DC = str(self.count_D)
  1452. self.count_T = 0
  1453. self.img_list.clear()
  1454. # 获取文件夹内的图像列表
  1455. extenxion = ['/*.jpg', '/*.png', '/*.bmp', '/*.jpeg', '/*.tif', '/*.tiff']
  1456. for ext in extenxion:
  1457. self.img_list = self.img_list + glob.glob(folder + ext)
  1458. self.show_list()
  1459. with open(cur_path + "/last_open_dir.txt", "w") as dir_f:
  1460. dir_f.writelines(folder)
  1461. # print("write: ", folder,cur_path+"/last_open_dir.txt")
  1462. self.gsd_file = os.path.join(folder, "gsd.txt")
  1463. if os.path.exists(self.gsd_file):
  1464. with open(self.gsd_file, "r", encoding="utf-8") as f:
  1465. for line in f.readlines():
  1466. line = line.strip()
  1467. print("line: ", line)
  1468. if len(line.split(" ")) == 4:
  1469. shebei_, beicepin_, pici_, gsd_ = line.split(" ")
  1470. shebei = shebei_
  1471. beicepin = beicepin_
  1472. pici = pici_
  1473. gsd = gsd_
  1474. print("打开文件 找到shebei,beicepin,pici相匹配的gsd:", shebei_, beicepin_, pici_, gsd_)
  1475. break
  1476. else:
  1477. pici = os.path.basename(folder)
  1478. beicepin = os.path.basename(os.path.dirname(folder))
  1479. shebei = os.path.basename(os.path.dirname(os.path.dirname(folder)))
  1480. # print(folder, pici, shebei)
  1481. gsd = 0
  1482. self.shebei.setText(str(shebei))
  1483. self.beicepin.setText(str(beicepin))
  1484. self.pici.setText(str(pici))
  1485. # self.gsd.setText(str(gsd))
  1486. else:
  1487. self.statistic_msg("未选择图像文件夹,请重新选取图像文件夹")
  1488. # print("self.img_list,folder: ", self.img_list, folder)
  1489. # update img list
  1490. def show_list(self):
  1491. self.resultWidget.clear()
  1492. results = [str(i) for i in self.img_list]
  1493. self.resultWidget.addItems(results)
  1494. if len(self.img_list) > 0:
  1495. self.resultWidget.setCurrentRow(0) # 默认使图像列表中的第一项处于选中状态
  1496. self.WidgetClicked_default(0)
  1497. def WidgetClicked_default(self, row):
  1498. item = self.resultWidget.item(row)
  1499. # 单击触发槽函数
  1500. # print("WidgetClicked(item): ",item.text())
  1501. self.statistic_msg("默认选中" + item.text())
  1502. self.index = self.img_list.index(item.text())
  1503. self.show_image(item.text(), self.raw_video)
  1504. self.label_6.setText(f"{os.path.basename(item.text())}原图像")
  1505. def WidgetClicked(self, item):
  1506. # 单击触发槽函数
  1507. # print("WidgetClicked(item): ",item,item.text())
  1508. self.statistic_msg("选中" + item.text())
  1509. self.index = self.img_list.index(item.text())
  1510. self.show_image(item.text(), self.raw_video)
  1511. self.label_6.setText(f"{os.path.basename(item.text())}原图像")
  1512. ########### 系统信息返回 ##########
  1513. def listViewdoubleClicked(self, item):
  1514. # 双击触发槽函数
  1515. try:
  1516. msg = item.text().split("\t")[1]
  1517. self.statistic_msg(msg)
  1518. # 开启查看日志窗口 -- qt打开文件
  1519. if msg.find("训练") != -1:
  1520. import re
  1521. # path 根据 self.count进行筛选
  1522. c = re.search('\d+', msg).group()
  1523. if c == "1":
  1524. s = f'exp'
  1525. else:
  1526. s = f'exp{c}'
  1527. self.logger = LoggerWindow(mode=msg + ' 过程信息',
  1528. path=os.path.join(self.runs_path.replace('detect', 'train'), s,
  1529. 'train.txt')) # 新建对象
  1530. self.logger.ui.show()
  1531. # self.DC = "0" #我改的
  1532. elif msg.find("检测") != -1:
  1533. import re
  1534. # path 根据 self.count进行筛选
  1535. self.DC = re.search('\d+', msg).group()
  1536. s = f'exp' if self.DC == "1" else f'exp{self.DC}'
  1537. # path 根据 self.count进行筛选
  1538. # print(os.path.join(self.runs_path,s,'detect.txt'))
  1539. self.logger = LoggerWindow(mode=msg + ' 过程信息',
  1540. path=os.path.join(self.runs_path, s, 'detect.txt')) # 新建对象
  1541. self.logger.ui.show()
  1542. else:
  1543. self.statistic_msg("目前没有检测结果,需要先进行检测")
  1544. # self.DC = "0"
  1545. except Exception as e:
  1546. self.statistic_msg(repr(e))
  1547. def listViewClicked(self, item):
  1548. # 单击触发槽函数
  1549. try:
  1550. msg = item.text().split("\t")[1]
  1551. self.statistic_msg(msg)
  1552. if msg.find("检测") != -1:
  1553. import re
  1554. # path 根据 self.count进行筛选
  1555. self.DC = re.search('\d+', msg).group()
  1556. else:
  1557. self.statistic_msg("目前没有检测结果,需要先进行检测")
  1558. # self.DC = "0"
  1559. except Exception as e:
  1560. self.statistic_msg(repr(e))
  1561. ################### 纠错 -- Label_click_Mouse #########################
  1562. # pip install labelme
  1563. def open_labelme(self):
  1564. # 开一个进程
  1565. # 修正命令行
  1566. try:
  1567. # if self.DC == "0": #DC是检测次数
  1568. # self.labelme_thread.det_path = "" #os.path.join(self.runs_path,s,"jsons") #标注文件替换
  1569. if self.DC == "0":
  1570. self.statistic_msg("目前没有检测结果,需要先进行检测")
  1571. else:
  1572. s = f'exp' if self.DC == "1" else f'exp{self.DC}'
  1573. det_path = os.path.join(self.runs_path, s, "jsons")
  1574. src_path = os.path.dirname(self.img_list[0])
  1575. json_list = glob.glob(src_path + '/*.json') # 先将原来标注好的json文件改为gt_.json文件
  1576. has_gt = False
  1577. for jsonf in json_list:
  1578. extenxion = ['.jpg', '.png', '.bmp', '.jpeg', '.tif', '.tiff']
  1579. for ext in extenxion:
  1580. imgf = jsonf.replace(".json", ext)
  1581. if os.path.exists(imgf):
  1582. has_gt = True
  1583. break
  1584. if has_gt:
  1585. break
  1586. if has_gt:
  1587. MessageBox(self.closeButton, title='提示', text='该图片所在目录下标注文件,不能执行纠错操作',
  1588. time=1000, auto=True).exec_()
  1589. else:
  1590. self.labelme_thread.det_path = det_path
  1591. self.labelme_thread.src_path = src_path
  1592. self.labelme_thread.start()
  1593. except Exception as e:
  1594. self.statistic_msg(repr(e))
  1595. ############################ 结果导出 #######################################
  1596. def save_output(self): # 导出报表,调用WriteThread
  1597. try:
  1598. if self.DC == "0":
  1599. self.statistic_msg("目前没有检测结果,需要先进行检测")
  1600. else:
  1601. # 书写 excel 内容
  1602. msg = f"第{self.DC}次检测结果"
  1603. # if os.name == 'nt': #windows 系统
  1604. file_path, _ = QFileDialog.getSaveFileName(self, "保存 结果", ".", 'Excel(*.xlsx)')
  1605. if file_path != "":
  1606. # 传入参数 开启进程
  1607. self.write_thread.msg = msg
  1608. self.write_thread.imgs = self.img_list
  1609. self.write_thread.path = file_path
  1610. s = f'exp' if self.DC == "1" else f'exp{self.DC}'
  1611. scr = os.path.join(self.runs_path, s)
  1612. self.write_thread.src = scr
  1613. self.write_thread.gsd = float(self.gsd.text())
  1614. self.write_thread.start()
  1615. # self.write_xlsxwriter(msg,self.img_list,file_path)
  1616. # else: # os.name = posix
  1617. # file_path, _ = QFileDialog.getSaveFileName(self,"保存 结果",".",'txt(*.txt)')
  1618. # self.write_txt(msg,self.img_list,file_path)
  1619. ################
  1620. else:
  1621. self.statistic_msg("未选择报表文件,请重新选取报表路径")
  1622. except Exception as e:
  1623. self.statistic_msg(repr(e))
  1624. def write_xlsxwriter(self, msg):
  1625. if msg == "error":
  1626. self.statistic_msg(msg)
  1627. self.tips.close()
  1628. MessageBox(
  1629. self.closeButton, title='提示', text='结果导出错误,请重新导出', time=1000, auto=True).exec_()
  1630. self.show_image(self.plots_thread.img_name, self.out_video)
  1631. elif msg == "开始导出报表":
  1632. self.statistic_msg(msg)
  1633. self.tips = MessageBox(
  1634. self.closeButton, title='提示', text=f'请稍等,正在导出图表文件。。。', time=500, auto=False) # .exec_()
  1635. self.tips.show()
  1636. elif msg == "导出报表完成":
  1637. self.statistic_msg(msg)
  1638. self.tips.close()
  1639. if self.DC != "0":
  1640. MessageBox(
  1641. self.closeButton, title='提示',
  1642. text=f'第{self.DC}次检测结果图表格式文件导出成功,正在关闭提示窗口。。。', time=2000, auto=True).exec_()
  1643. else:
  1644. self.statistic_msg("目前没有检测结果,需要先进行检测")
  1645. ############################## 模型操作 ##############################
  1646. def begin_action(self):
  1647. if self.t_flag == 2:
  1648. self.model_type = os.path.join(self.model_path, self.comboBox.currentText())
  1649. # self.model_type = os.path.join(self.model_path,"yolov5s-seg.pt")#我改为训练只使用预训练模型
  1650. self.train_thread.model = self.model_type
  1651. # 模型训练 ##
  1652. if self.ui_train.radioButton_3.isChecked(): # 增加样本再训练
  1653. if len(self.img_list):
  1654. save_dirs = Path('./dataset') # fix path
  1655. Path(save_dirs / 'labels').mkdir(parents=True, exist_ok=True)
  1656. Path(save_dirs / 'images').mkdir(parents=True, exist_ok=True)
  1657. for img_name in self.img_list:
  1658. dirs = os.path.dirname(img_name)
  1659. name = os.path.basename(img_name)
  1660. jsons = name.replace(name.split('.')[-1], 'json')
  1661. if os.path.exists(os.path.join(dirs, jsons)):
  1662. # 转换格式 -- 摸一个文件夹
  1663. self.statistic_msg(f"{img_name}图像成功加入训练集中")
  1664. convert_json_label_to_yolov_seg_label(os.path.join(dirs, jsons), dirs,
  1665. os.path.join(str(save_dirs / 'labels'),
  1666. jsons.replace(".json", ".txt")))
  1667. shutil.copyfile(img_name, os.path.join(str(save_dirs / 'images'), name))
  1668. else:
  1669. self.statistic_msg(f"{img_name}图像没有对应的标注文件,请先进行检测或人工标注后再训练模型")
  1670. # 解除冻结
  1671. self.trainbutton.setEnabled(True)
  1672. return None
  1673. else:
  1674. self.statistic_msg(f"请选择要新增加的图像数据")
  1675. # 解除冻结
  1676. self.trainbutton.setEnabled(True)
  1677. return None
  1678. self.count_T += 1
  1679. self.train_thread.start() # 启动子进程 -- 注意命令行的中 python 解释器 的选择 传递一个结束信号
  1680. self.statistic_msg(f"第{self.count_T}次训练中")
  1681. # 解除冻结
  1682. self.trainbutton.setEnabled(True)
  1683. if self.t_flag == 0:
  1684. self.model_type = os.path.join(self.model_path, self.comboBox.currentText())
  1685. self.det_thread.model = self.model_type
  1686. try:
  1687. if len(self.img_list) == 0:
  1688. self.statistic_msg("目前没有检测结果,需要先进行检测")
  1689. return
  1690. elif len(self.img_list) > 1:
  1691. self.det_thread.data = os.path.dirname(self.img_list[0])
  1692. else:
  1693. self.det_thread.data = self.img_list[0]
  1694. self.count_D += 1
  1695. self.det_thread.start() # 启动子进程 -- 注意命令行的中 python 解释器 的选择
  1696. self.statistic_msg(f"第{self.count_D}次检测中")
  1697. self.DC = str(self.count_D)
  1698. except Exception as e:
  1699. self.statistic_msg(repr(e))
  1700. self.detectbutton.setEnabled(True)
  1701. if self.t_flag == 1:
  1702. self.py_type = os.path.join(self.feature_alg, self.comboBox.currentText())
  1703. self.fea_thread.py = self.py_type
  1704. # self.fea_thread.send_msg.connect(lambda x: self.show_msg3(x))
  1705. try:
  1706. if len(self.img_list) == 0:
  1707. self.statistic_msg("目前没有识别结果,需要先进行识别")
  1708. return
  1709. elif len(self.img_list) > 1:
  1710. self.fea_thread.data = os.path.dirname(self.img_list[0])
  1711. else:
  1712. self.fea_thread.data = self.img_list[0]
  1713. self.count_D += 1
  1714. self.fea_thread.start() # 启动子进程 -- 注意命令行的中 python 解释器 的选择
  1715. script_path = self.py_type
  1716. image_path_list = self.img_list
  1717. # 循环遍历选择的文件夹内的图片,并传入所选算法中
  1718. self.statistic_msg("处理中......")
  1719. for path in image_path_list:
  1720. result = subprocess.run(['python', script_path, path], capture_output=True, text=True)
  1721. self.output_file_list[path] = result.stdout.replace('\n', '')
  1722. self.statistic_msg(f"处理完成!")
  1723. # self.statistic_msg(f"第{self.count_D}次识别中")
  1724. self.DC = str(self.count_D)
  1725. except Exception as e:
  1726. self.statistic_msg(repr(e))
  1727. self.trainbutton_2.setEnabled(True)
  1728. if self.t_flag == 3 or self.t_flag == 4:
  1729. self.py_type = os.path.join(self.feature_alg, self.comboBox.currentText())
  1730. self.fea_thread.py = self.py_type
  1731. try:
  1732. if len(self.img_list) == 0:
  1733. self.statistic_msg("目前没有识别结果,需要先进行识别")
  1734. return
  1735. elif len(self.img_list) > 1:
  1736. self.fea_thread.data = os.path.dirname(self.img_list[0])
  1737. else:
  1738. self.fea_thread.data = self.img_list[0]
  1739. self.count_D += 1
  1740. self.fea_thread.start() # 启动子进程 -- 注意命令行的中 python 解释器 的选择
  1741. script_path = self.py_type
  1742. img_list = self.img_list
  1743. my_path_pic_0 = [os.path.dirname(path) for path in img_list]
  1744. my_path_pic = my_path_pic_0[0]
  1745. label_path = os.path.dirname(my_path_pic)
  1746. my_label_path = os.path.join(label_path, "picture_labels")
  1747. print(my_label_path)
  1748. label_path_list = os.listdir(my_label_path)
  1749. self.statistic_msg(f"开始检测")
  1750. for img_path in img_list:
  1751. img_name = os.path.basename(img_path).split('.')[0]
  1752. for label in label_path_list:
  1753. label_name = os.path.basename(label).split('.')[0]
  1754. if img_name == label_name:
  1755. s_label_path = os.path.join(my_label_path, label)
  1756. result = subprocess.run(['python', script_path, img_path, s_label_path], capture_output=True, text=True)
  1757. self.output_file_list[img_path] = result.stdout.replace('\n', '')
  1758. break
  1759. self.statistic_msg(f"第{self.count_D}次识别中")
  1760. self.DC = str(self.count_D)
  1761. except Exception as e:
  1762. self.statistic_msg(repr(e))
  1763. self.trainbutton_3.setEnabled(True)
  1764. import configparser
  1765. # ## 密码登录
  1766. class Welcome(QMainWindow):
  1767. success = pyqtSignal(bool) # 因为用了os 后台 发送不了中间进程 就只有 启动和开始
  1768. def __init__(self):
  1769. super(Welcome, self).__init__()
  1770. self.login = loadUi('qt_win/login-form2.ui')
  1771. self.register = loadUi('qt_win/register-form2.ui')
  1772. self.login.setWindowTitle("登陆界面")
  1773. self.register.setWindowTitle("注册界面")
  1774. self.login.b1.clicked.connect(self.Login)
  1775. self.login.b2.clicked.connect(self.show_reg)
  1776. self.register.b3.clicked.connect(self.reg)
  1777. self.register.b4.clicked.connect(self.show_login)
  1778. self.IsRememberUser()
  1779. self.config_json = "config.json" # 保存信息的json文件
  1780. '''读取json -- 解析账号'''
  1781. def get_information(self):
  1782. if not os.path.exists(self.config_json):
  1783. return 0
  1784. with open(self.config_json, 'r') as f:
  1785. json_data = json.load(f)
  1786. users = json_data.keys()
  1787. if self.account not in users:
  1788. return 0
  1789. if self.passwd != json_data[self.account]:
  1790. return 1
  1791. else:
  1792. return 2
  1793. '''读写json -- 解析账号'''
  1794. def set_information(self):
  1795. if not os.path.exists(self.config_json):
  1796. # 不存在 新建
  1797. data = {} # key -- id ; value -- password
  1798. else:
  1799. # 读取
  1800. with open(self.config_json, 'r') as f:
  1801. data = json.load(f)
  1802. # 判断
  1803. if self.re_account in data.keys():
  1804. return 0
  1805. if self.re_passwd != self.confirm_passwd:
  1806. return 1
  1807. # 写入文件
  1808. data[self.re_account] = self.re_passwd
  1809. with open(self.config_json, "w") as f:
  1810. json.dump(data, f)
  1811. return 2
  1812. """设置记住密码"""
  1813. def IsRememberUser(self):
  1814. config = configparser.ConfigParser()
  1815. if not os.path.exists('user.ini'):
  1816. self.login.c.setChecked(False)
  1817. else:
  1818. file = config.read('user.ini') # 读取密码账户的配置文件
  1819. config_dict = config.defaults() # 返回包含实例范围默认值的字典
  1820. if config_dict['remember'] == 'True': # 如果user.ini文件存在,并且里面设置了记住密码,就将默认的用户名和密码取出来,自动填充在用户界面上
  1821. self.account = config_dict['user_name'] # 获取账号信息
  1822. self.login.tb1.setText(self.account) # 写入账号上面
  1823. self.passwd = config_dict['password']
  1824. self.login.tb2.setText(self.passwd)
  1825. self.login.c.setChecked(True)
  1826. else:
  1827. self.login.tb1.setText("")
  1828. self.login.tb2.setText("")
  1829. self.login.c.setChecked(False)
  1830. """设置配置文件格式,设置默认用户名和密码,登录成功后,将用户填写的用户名和密码写入user.ini文件中"""
  1831. def config_ini(self):
  1832. self.account = self.login.tb1.text()
  1833. self.passwd = self.login.tb2.text()
  1834. config = configparser.ConfigParser()
  1835. if self.login.c.isChecked():
  1836. config["DEFAULT"] = {
  1837. "user_name": self.account,
  1838. "password": self.passwd,
  1839. "remember": self.login.c.isChecked()
  1840. }
  1841. else:
  1842. config["DEFAULT"] = {
  1843. "user_name": self.account,
  1844. "password": "",
  1845. "remember": self.login.c.isChecked()
  1846. }
  1847. with open('user.ini', 'w') as configfile:
  1848. config.write((configfile))
  1849. def Login(self):
  1850. self.account = self.login.tb1.text()
  1851. self.passwd = self.login.tb2.text()
  1852. if len(self.account) == 0 or len(self.passwd) == 0:
  1853. MessageBox(
  1854. self.login, title='提示', text='用户名或密码为空!', time=2000, auto=True).exec_()
  1855. else:
  1856. info = self.get_information()
  1857. if info == 0:
  1858. MessageBox(
  1859. self.login, title='提示', text='此用户不存在!', time=2000, auto=True).exec_()
  1860. # QMessageBox.information(self,"Login Output","Invalid User, Register for new user")
  1861. elif info == 1:
  1862. MessageBox(
  1863. self.login, title='提示', text='用户名和密码不匹配!', time=2000, auto=True).exec_()
  1864. # QMessageBox.information(self,"Login Output","Error! User and password do not match")
  1865. else:
  1866. MessageBox(
  1867. self.login, title='提示', text='恭喜!登录成功!', time=2000, auto=True).exec_()
  1868. # QMessageBox.information(self,"Login Output","Congrats! you login successfully")
  1869. self.config_ini() # 加载用户密码配置文件
  1870. # 关闭窗口
  1871. self.login.close()
  1872. self.success.emit(True)
  1873. # 注册
  1874. def show_reg(self):
  1875. self.login.close()
  1876. self.register.tb3.setText("") #
  1877. self.register.tb4.setText("") #
  1878. self.register.tb5.setText("") #
  1879. self.register.show()
  1880. def reg(self):
  1881. self.re_account = self.register.tb3.text()
  1882. self.re_passwd = self.register.tb4.text()
  1883. self.confirm_passwd = self.register.tb5.text()
  1884. if len(self.re_account) == 0 or len(self.re_passwd) == 0 or len(self.confirm_passwd) == 0:
  1885. MessageBox(
  1886. self.register, title='提示', text='用户名或密码为空!', time=2000, auto=True).exec_()
  1887. else:
  1888. info = self.set_information()
  1889. if info == 0:
  1890. MessageBox(
  1891. self.register, title='提示', text='此用户名已经存在了!', time=2000, auto=True).exec_()
  1892. # QMessageBox.information(self,"Register Output", "The user already registered, Try another username")
  1893. elif info == 1:
  1894. MessageBox(
  1895. self.register, title='提示', text='两次密码不一致!', time=2000, auto=True).exec_()
  1896. # QMessageBox.information(self,"Register Output", "Error! The passwords entered twice do not match")
  1897. else:
  1898. MessageBox(
  1899. self.register, title='提示', text='注册成功,现在可以登录了!', time=2000, auto=True).exec_()
  1900. # QMessageBox.information(self,"Register Output", "The user registered successfully, You can login now!!!")
  1901. self.show_login() # 返回登录
  1902. def show_login(self):
  1903. self.register.close()
  1904. self.login.tb1.setText("") #
  1905. self.login.tb2.setText("") #
  1906. self.login.show()
  1907. if __name__ == '__main__':
  1908. app = QApplication(sys.argv)
  1909. # wel = Welcome()
  1910. # wel.login.show()
  1911. win = Window()
  1912. # win.show()
  1913. sys.exit(app.exec_())