415 lines
17 KiB
Python
415 lines
17 KiB
Python
import time
|
||
|
||
from PyQt5.QtGui import QPixmap, QImage
|
||
from PyQt5.QtWidgets import QWidget, QFileDialog,QMainWindow, QLabel, QSizePolicy, QApplication, QAction, QHBoxLayout
|
||
from PyQt5.QtCore import *
|
||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||
import sys
|
||
import traceback
|
||
|
||
import ctypes as C
|
||
import numpy as np
|
||
import cv2
|
||
import os
|
||
from test_0415_ui import Ui_MainWindow
|
||
from pyueye import ueye
|
||
import numpy as np
|
||
import cv2
|
||
# 串口通信
|
||
import serial
|
||
import time
|
||
import threading
|
||
import torch
|
||
from Class.Camera import Camera_class
|
||
from Class.Motor import Motor_class
|
||
from Class.Pan import Pan_class
|
||
from Class.Yolo import yolo_class
|
||
|
||
|
||
|
||
class img_yolo(QtCore.QThread):
|
||
sinOut = pyqtSignal(str) # 聲明一個帶字串參數的信號
|
||
|
||
def __init__(self, yolo_model, parent=None):
|
||
super().__init__(parent)
|
||
self.yolo = yolo_model
|
||
|
||
def normalize_image(self, img):
|
||
if img is not None:
|
||
normalized_image = cv2.normalize(img, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)
|
||
return normalized_image
|
||
return None
|
||
|
||
def process_image(self, img, filename):
|
||
t1 = time.time()
|
||
results = self.yolo.YoloDetect(img)
|
||
t2 = time.time()
|
||
print(f'推論時間 = {t2 - t1}')
|
||
print(f'{filename}')
|
||
|
||
# 如果有預測結果,則打印信心值
|
||
if results.pred[0] is not None and len(results.pred[0]) > 0:
|
||
conf = results.pred[0][0, 4] # 信心值
|
||
print(f'信心值 = {conf}')
|
||
|
||
if results.pred[0] is None or len(results.pred[0]) == 0:
|
||
return
|
||
|
||
predicted_classes = results.names[int(results.pred[0][0, -1])]
|
||
if predicted_classes == 'fisheyes':
|
||
processed_img = self.fisheyes_method(results,filename,img)
|
||
elif predicted_classes == 'gel':
|
||
# print('2')
|
||
processed_img = self.gel_method(results,filename,img)
|
||
elif predicted_classes == 'scratches':
|
||
# print('3')
|
||
processed_img = self.scratches_method(results,filename,img)
|
||
# 魚眼處理
|
||
def fisheyes_method(self, results, filename, img):
|
||
conf = results.pred[0][0, 4]
|
||
predicted_classes = results.names[int(results.pred[0][0, -1])]
|
||
box = results.pred[0][0, :4].cpu().numpy().astype(int)
|
||
roi = img[box[1]:box[3], box[0]:box[2]]
|
||
normalized_roi = self.normalize_image(roi)
|
||
gray_roi = cv2.cvtColor(normalized_roi, cv2.COLOR_BGR2GRAY)
|
||
clahe = cv2.createCLAHE(clipLimit=0.0, tileGridSize=(1, 1))
|
||
clahe_image = clahe.apply(gray_roi)
|
||
_, binary_image = cv2.threshold(clahe_image, 200, 255, cv2.THRESH_BINARY)
|
||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (6, 6))
|
||
closed_image = cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel)
|
||
contours, _ = cv2.findContours(closed_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||
|
||
if len(contours) > 0:
|
||
max_contour = max(contours, key=cv2.contourArea)
|
||
if len(max_contour) > 5:
|
||
ellipse = cv2.fitEllipse(max_contour)
|
||
contour_image = roi.copy()
|
||
cv2.ellipse(contour_image, ellipse, (0, 255, 0), 1)
|
||
# center, axes, angle = ellipse
|
||
# major_axis = max(axes)
|
||
# longest_distance = 2 * major_axis * 5.5 / 1000
|
||
|
||
# 创建与图像相同大小的空白掩膜
|
||
mask = np.zeros_like(binary_image)
|
||
# 在掩膜上绘制填充了的椭圆
|
||
cv2.ellipse(mask, ellipse, (255), thickness=cv2.FILLED)
|
||
# 将掩膜应用于原始二值图像以获取椭圆内部的像素
|
||
ellipse_interior = cv2.bitwise_and(binary_image, binary_image, mask=mask)
|
||
# 计算椭圆内部区域的像素总数,即面积
|
||
interior_area_pixels = np.sum(ellipse_interior == 255)
|
||
interior_area_mm2 = interior_area_pixels * (5.5 ** 2) / 1000 # 将像素转换为平方毫米
|
||
# print(f"椭圆內部面積 (像素): {interior_area_pixels}")
|
||
print(f"椭圆內部面積 (平方毫米): {interior_area_mm2}")
|
||
|
||
# cv2.line(contour_image, (int(center[0] - major_axis / 2), int(center[1])),
|
||
# (int(center[0] + major_axis / 2), int(center[1])), (0, 0, 255), 1)
|
||
# print(f"最長距離:{longest_distance} mm")
|
||
self.save_images(filename, roi, results, conf)
|
||
# return contour_image
|
||
|
||
# 凝膠處理
|
||
def gel_method(self, results, filename, img):
|
||
conf = results.pred[0][0, 4]
|
||
predicted_classes = results.names[int(results.pred[0][0, -1])]
|
||
box = results.pred[0][0, :4].cpu().numpy().astype(int)
|
||
roi = img[box[1]:box[3], box[0]:box[2]]
|
||
normalized_roi = self.normalize_image(roi)
|
||
|
||
gray_roi = cv2.cvtColor(normalized_roi, cv2.COLOR_BGR2GRAY)
|
||
clahe = cv2.createCLAHE(clipLimit=0.0, tileGridSize=(1, 1))
|
||
clahe_image = clahe.apply(gray_roi)
|
||
_, binary_image = cv2.threshold(clahe_image, 170, 255, cv2.THRESH_BINARY)
|
||
# 使用 cv2.RETR_TREE 检索完整的轮廓层级
|
||
contours, _ = cv2.findContours(binary_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||
|
||
total_area_pixels = 0
|
||
|
||
for i, contour in enumerate(contours):
|
||
# 绘制填充了的轮廓
|
||
# cv2.drawContours(roi, [contour], -1, (255, 255, 255), thickness=cv2.FILLED)
|
||
|
||
# 计算轮廓的面积并加到总面积中
|
||
contour_area_pixels = cv2.contourArea(contour)
|
||
total_area_pixels += contour_area_pixels
|
||
|
||
contour_area_mm2 = contour_area_pixels * (5.5 ** 2) / 1000 # 将像素转换为毫米
|
||
# print(f"轮廓 {i + 1} 的面积 (像素): {contour_area_pixels}")
|
||
# print(f"轮廓 {i + 1} 的面积 (平方毫米): {contour_area_mm2}")
|
||
|
||
# 计算总面积并将单位转换为毫米
|
||
total_area_mm2 = total_area_pixels * (5.5 ** 2) / 1000 # 将像素转换为毫米
|
||
# print(f"總面積 (像素): {total_area_pixels}")
|
||
print(f"gel面積 (平方毫米): {total_area_mm2}")
|
||
|
||
# 在原始图像上绘制轮廓
|
||
contour_image = roi.copy()
|
||
cv2.drawContours(contour_image, contours, -1, (0, 255, 0), 1)
|
||
|
||
self.save_images(filename, roi, results, conf)
|
||
|
||
# if len(contours) > 0:
|
||
# max_contour = max(contours, key=cv2.contourArea)
|
||
# if len(max_contour) > 5:
|
||
# ellipse = cv2.fitEllipse(max_contour)
|
||
# contour_image = roi.copy()
|
||
# cv2.ellipse(contour_image, ellipse, (0, 255, 0), 1)
|
||
# center, axes, angle = ellipse
|
||
# major_axis = max(axes)
|
||
# longest_distance = 2 * major_axis * 5.5 / 1000
|
||
# cv2.line(contour_image, (int(center[0] - major_axis / 2), int(center[1])),
|
||
# (int(center[0] + major_axis / 2), int(center[1])), (0, 0, 255), 1)
|
||
# print(f"最長距離:{longest_distance} mm")
|
||
# self.save_images(filename, contour_image, results, conf)
|
||
# return contour_image
|
||
|
||
# 刮痕處理
|
||
def scratches_method(self,results,filename,img):
|
||
# conf = results.pred[0][0, 4]
|
||
# predicted_classes = results.names[int(results.pred[0][0, -1])]
|
||
# box = results.pred[0][0, :4].cpu().numpy().astype(int)
|
||
# roi = img[box[1]:box[3], box[0]:box[2]]
|
||
# normalized_roi = self.normalize_image(roi)
|
||
# gray_roi = cv2.cvtColor(normalized_roi, cv2.COLOR_BGR2GRAY)
|
||
# clahe = cv2.createCLAHE(clipLimit=0.0, tileGridSize=(1, 1))
|
||
# clahe_image = clahe.apply(gray_roi)
|
||
# _, binary_image = cv2.threshold(clahe_image, 170, 255, cv2.THRESH_BINARY)
|
||
# kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (6, 6))
|
||
# closed_image = cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel)
|
||
# contours, _ = cv2.findContours(closed_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||
# self.save_images(filename, normalized_roi, results, conf)
|
||
|
||
conf = results.pred[0][0, 4]
|
||
predicted_classes = results.names[int(results.pred[0][0, -1])]
|
||
box = results.pred[0][0, :4].cpu().numpy().astype(int)
|
||
roi = img[box[1]:box[3], box[0]:box[2]]
|
||
# normalized_roi = self.normalize_image(roi)
|
||
gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
|
||
# clahe = cv2.createCLAHE(clipLimit=0.0, tileGridSize=(1, 1))
|
||
# clahe_image = clahe.apply(gray_roi)
|
||
_, binary_image = cv2.threshold(gray_roi, 45, 240, cv2.THRESH_BINARY)
|
||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (8, 8))
|
||
closed_image = cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel)
|
||
contours, _ = cv2.findContours(binary_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||
|
||
total_area_pixels = 0
|
||
|
||
for i, contour in enumerate(contours):
|
||
# 绘制填充了的轮廓
|
||
# cv2.drawContours(roi, [contour], -1, (255, 255, 255), thickness=cv2.FILLED)
|
||
|
||
# 计算轮廓的面积并加到总面积中
|
||
contour_area_pixels = cv2.contourArea(contour)
|
||
total_area_pixels += contour_area_pixels
|
||
|
||
contour_area_mm2 = contour_area_pixels * (5.5 ** 2) / 1000 # 将像素转换为毫米
|
||
# print(f"轮廓 {i + 1} 的面积 (像素): {contour_area_pixels}")
|
||
# print(f"轮廓 {i + 1} 的面积 (平方毫米): {contour_area_mm2}")
|
||
|
||
# 计算总面积并将单位转换为毫米
|
||
total_area_mm2 = total_area_pixels * (5.5 ** 2) / 1000 # 将像素转换为毫米
|
||
# print(f"總面積 (像素): {total_area_pixels}")
|
||
print(f"scratches面積 (平方毫米): {total_area_mm2}")
|
||
|
||
# 在原始图像上绘制轮廓
|
||
contour_image = roi.copy()
|
||
cv2.drawContours(contour_image, contours, -1, (0, 255, 0), 1)
|
||
|
||
self.save_images(filename, roi, results, conf)
|
||
|
||
# if len(contours) > 0:
|
||
# max_contour = max(contours, key=cv2.contourArea)
|
||
# if len(max_contour) > 5:
|
||
# ellipse = cv2.fitEllipse(max_contour)
|
||
# contour_image = roi.copy()
|
||
# cv2.ellipse(contour_image, ellipse, (0, 255, 0), 1)
|
||
# center, axes, angle = ellipse
|
||
# major_axis = max(axes)
|
||
# longest_distance = 2 * major_axis * 5.5 / 1000
|
||
# cv2.line(contour_image, (int(center[0] - major_axis / 2), int(center[1])),
|
||
# (int(center[0] + major_axis / 2), int(center[1])), (0, 0, 255), 1)
|
||
# print(f"最長距離:{longest_distance} mm")
|
||
# self.save_images(filename, contour_image, results, conf)
|
||
# return contour_image
|
||
|
||
def save_images(self, filename, contour_image, results, conf):
|
||
if conf > 0.8:
|
||
base_name, ext = os.path.splitext(filename)
|
||
new_file_name = f'{base_name}_2{ext}'
|
||
print(f'Saving {new_file_name}')
|
||
cv2.imwrite(f'img_2/{new_file_name}', contour_image)
|
||
|
||
# if conf > 0.6:
|
||
# resized_frame_high_conf = cv2.resize(results.render()[0], (1280, 1280))
|
||
# print(f'Saving {filename}')
|
||
# cv2.imwrite(f'img_2/{filename}', resized_frame_high_conf)
|
||
|
||
def run(self):
|
||
print('run')
|
||
self.img_list = []
|
||
self.filename_list = []
|
||
for filename in os.listdir(r"./image_folder"):
|
||
if filename.endswith('.jpg') or filename.endswith('.bmp'):
|
||
img = cv2.imread('image_folder' + "/" + filename)
|
||
self.img_list.append(img)
|
||
self.filename_list.append(filename)
|
||
print(f'img讀取完成')
|
||
for i in range(0, len(self.filename_list)):
|
||
img = self.img_list[i]
|
||
file_name = self.filename_list[i]
|
||
self.process_image(img, file_name)
|
||
|
||
|
||
def img_to_view(img):#原圖
|
||
img= cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # QT顏色顯示轉換
|
||
Ny, Nx, _ = img.shape
|
||
img = QtGui.QImage(img.data, Nx, Ny,Nx*3, QtGui.QImage.Format_RGB888) #須改格式
|
||
return img
|
||
|
||
class MainWindow(QMainWindow,Ui_MainWindow):
|
||
def __init__(self, parent=None): #按鍵設定
|
||
super(MainWindow, self).__init__(parent)
|
||
self.setupUi(self)
|
||
# self.view_1.setScaledContents(True)
|
||
# self.view_2.setScaledContents(True)
|
||
# self.view_3.setScaledContents(True)
|
||
# self.view_4.setScaledContents(True)
|
||
|
||
self.bt_1.clicked.connect(self.bt_1_clicked)
|
||
# self.bt_2.clicked.connect(self.bt_2_clicked) # 連接按鈕到新的功能
|
||
self.bt_3.clicked.connect(self.bt_3_clicked)
|
||
|
||
# self.cam = Camera_class()
|
||
# self.cam.start()
|
||
# self.cam.rawdata.connect(self.show_img)
|
||
#
|
||
# self.run_flag = True
|
||
# self.com5 = Motor_class()
|
||
# self.com5.start()
|
||
# self.com5.sinOut.connect(self.com5_str)
|
||
#
|
||
#
|
||
# self.com4 = Pan_class()
|
||
# self.com4.start()
|
||
# self.com4.sinOut.connect(self.com4_str)
|
||
# self.count = 0
|
||
# self.img_count=0
|
||
|
||
self.yolo = yolo_class('weight/best.pt')
|
||
#self.yolo.start()
|
||
# self.img_test_2()
|
||
|
||
|
||
def show_img(self,img):
|
||
self.img = img
|
||
h,w,_ = self.img.shape
|
||
img= img_to_view(img)
|
||
self.view_1.setPixmap(QtGui.QPixmap.fromImage(img))
|
||
|
||
def bt_1_clicked(self):
|
||
self.time_2= time.time()
|
||
self.com5.start_run()
|
||
self.run_flag = True
|
||
self.time_1 = time.time()
|
||
|
||
def com5_str(self,str_1):
|
||
if str_1 =='move':
|
||
self.count = self.count+1
|
||
if self.count< 6:
|
||
print(self.count)
|
||
if str_1 == 'move':
|
||
self.img_count = 0
|
||
self.com4.move()
|
||
if self.count == 5:
|
||
self.run_flag = False
|
||
if str_1 =='2':
|
||
# self.img_test()
|
||
#self.time_1 = time.time()
|
||
#print(f's={self.time_1-self.time_2}')
|
||
# print('save_img')
|
||
self.img_count = self.img_count+1
|
||
cv2.imwrite(f'image_folder\\{str(self.count)}_{self.img_count}.jpg',self.img)
|
||
#self.time_2= time.time()
|
||
|
||
def com4_str(self,str_1):
|
||
if str_1 =='move_end' and self.run_flag == True:
|
||
self.com5.start_run()
|
||
if str_1 == 'Motor emergency stop 010':
|
||
print('010')
|
||
self.run_flag = False
|
||
self.count = 0
|
||
time_2 = time.time()
|
||
print(f'all_time - {time_2 - self.time_1}')
|
||
self.bt_3_clicked()
|
||
|
||
def img_test_2(self):
|
||
img_list=[]
|
||
for filename in os.listdir(r"./image_folder"):
|
||
if filename.endswith('.jpg'):
|
||
img = cv2.imread('image_folder' + "/" + filename)
|
||
img_list.append(img)
|
||
# self.img_test(img, filename)
|
||
print(f'img讀取完成')
|
||
run_list=[]
|
||
for img in img_list:
|
||
run_list.append(img_yolo( self.yolo,img))
|
||
for i in range (0,len(run_list)):
|
||
run_list[i].start()
|
||
|
||
def bt_3_clicked(self):
|
||
self.T1 = img_yolo(self.yolo)
|
||
self.T1.finished.connect(self.on_yolo_finished) # Connect to finished signal
|
||
self.T1.start()
|
||
|
||
def on_yolo_finished(self):
|
||
img_confidence = {} # 用于存储图像的置信度值
|
||
|
||
# 从img_2文件夹加载图像
|
||
for filename in os.listdir(r"./img_2"):
|
||
if filename.endswith('.jpg'):
|
||
img_path = os.path.join("./img_2", filename)
|
||
img = cv2.imread(img_path)
|
||
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
||
h, w, _ = img_rgb.shape
|
||
qimg = QtGui.QImage(img_rgb.data, w, h, w * 3, QtGui.QImage.Format_RGB888)
|
||
|
||
# 执行YOLO检测并获取置信度值
|
||
results = self.yolo.YoloDetect(img)
|
||
if results.pred[0] is not None and len(results.pred[0]) > 0:
|
||
conf = results.pred[0][0, 4] # 置信度值
|
||
else:
|
||
conf = 0
|
||
|
||
img_confidence[filename] = conf
|
||
|
||
# 按置信度值降序排列图像
|
||
sorted_imgs = sorted(img_confidence.items(), key=lambda x: x[1], reverse=True)
|
||
|
||
count = 0 # 计算显示的图像数量
|
||
# 基于置信度值在view_2、view_3和view_4中显示图像
|
||
for filename, _ in sorted_imgs:
|
||
img_path = os.path.join("./img_2", filename)
|
||
img = cv2.imread(img_path)
|
||
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
||
h, w, _ = img_rgb.shape
|
||
qimg = QtGui.QImage(img_rgb.data, w, h, w * 3, QtGui.QImage.Format_RGB888)
|
||
|
||
if not self.view_2.pixmap():
|
||
self.view_2.setPixmap(QtGui.QPixmap.fromImage(qimg))
|
||
count += 1
|
||
elif not self.view_3.pixmap():
|
||
self.view_3.setPixmap(QtGui.QPixmap.fromImage(qimg))
|
||
count += 1
|
||
elif not self.view_4.pixmap():
|
||
self.view_4.setPixmap(QtGui.QPixmap.fromImage(qimg))
|
||
count += 1
|
||
|
||
# 如果显示了3张图像,则退出循环
|
||
if count == 3:
|
||
break
|
||
|
||
|
||
if __name__ == "__main__":
|
||
app = QApplication(sys.argv)
|
||
window = MainWindow()
|
||
window.show()
|
||
sys.exit(app.exec_()) |