screwdriver/screwdriver_test.py
2025-02-06 16:10:58 +08:00

508 lines
25 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import copy
import threading
from datetime import datetime, timedelta
import re
import time
import PyQt5
import numpy as np
import os
import sys
from PyQt5.QtGui import QImage, QPixmap, QColor
from PyQt5.QtWidgets import QFileDialog, QApplication, QTableWidgetItem, QHeaderView, QGraphicsScene, \
QGraphicsPixmapItem, QGraphicsRectItem
import cv2
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal, QObject, Qt
from screwdriver_test_Window import Ui_MainWindow
from ScrewDrive import ScrewDrive
from ScrewDrive import Measure
import logging
from Myini import Myini
"""
python 3.6
pip install tensorflow-gpu==2.6.0
pip install keras==2.6.0
pip install opencv-python==3.4.2.16
pip install matplotlib==3.3.4
"""
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, QObject):
def __init__(self):
QtWidgets.QMainWindow.__init__(self) # 創建主界面對象
self.setupUi(self)
#self.imageLable.setScaledContents(True)
#self.imageLable_2.setScaledContents(True)
self.iniLogging()
self.dev_logger.info('screwdriver_test --> Start')
self.MYINI = Myini()
self.comboBox_recipe.addItems([recipe for recipe in self.MYINI.recipes])
# self.dbwindow = DB_MainWindow()
self.label_Image.setScaledContents(True)
self.iniGuiEvent()
self.tableWidget_information.setColumnCount(4)
self.tableWidget_information.setRowCount(10)
self.label_image_information.setScaledContents(True)
self.label_image_histogram.setScaledContents(True)
self.label_image_Remedy.setScaledContents(True)
self.label_image_extend.setScaledContents(True)
self.tableWidget_information_correctrate.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# self.tableWidget_information_correctrate.horizontalHeader().setSectionResizeMode(0, QHeaderView.Interactive)
self.tableWidget_information.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
# self.tableWidget_information.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
self.tableWidget_information.setSortingEnabled(True)
# self.tableWidget_information.horizontalHeader().setSectionResizeMode(0, QHeaderView.Interactive)
self.comboBox_models.addItems(os.listdir(r'D:\ScrewdriverFile\model\0509\13'))
self.comboBox_Validation_set.addItems(os.listdir(r'D:\ScrewdriverFile\ScrewdriverImage\Varification'))
self.ClassNumber = 0
self.inputimage_filename = ''
# self.screwdrive.eventManager.AddEventListener(self.screwdrive.EVENT_ARTICAL, )
# self.screwdrive.eventManager.Start()
self.screwdrive = ScrewDrive(dev_logger=self.dev_logger, myIni=self.MYINI)
self.screwdrive.trigger.connect(self.receive_message)
self.screwdrive.finish.connect(self.receive_message_Finish)
self.screwdrive.Measure_Step.connect(self.measure_step)
self.screwDrive_for_test = ScrewDrive_For_Test(myIni=self.MYINI)
self.screwDrive_for_test.trigger.connect(self.receive_message)
self.screwDrive_for_test.finish.connect(self.receive_message_Finish)
def iniGuiEvent(self):
self.comboBox_recipe.currentIndexChanged.connect(self.Recipe_Change)
self.btn_testmain.clicked.connect(self.Main)
self.btn_testmeasure.clicked.connect(self.Test_Measure)
self.btn_Measure.clicked.connect(self.Measure)
self.btn_softwareTest.clicked.connect(self.softwareTest)
self.tableWidget_information.cellClicked.connect(self.ShowImage)
self.comboBox_AI_Class.currentIndexChanged.connect(self.AI_Class_Changed)
def iniLogging(self):
logging_FileName = time.strftime('%Y%m%d')
self.dev_logger: logging.Logger = logging.getLogger(name='dev')
self.dev_logger.setLevel(logging.DEBUG)
handler: logging.StreamHandler = logging.StreamHandler()
formatter: logging.Formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(message)s')
handler.setFormatter(formatter)
self.dev_logger.addHandler(handler)
file_handler: logging.FileHandler = logging.FileHandler(rf'.\Logging\screwdriver_test\{logging_FileName}_dev.log', mode='a')
file_handler.setFormatter(formatter)
self.dev_logger.addHandler(file_handler)
def Recipe_Change(self):
try:
self.MYINI.recipe_change(self.comboBox_recipe.currentText())
self.dev_logger.info(f'Recipe_Change {self.MYINI.recipe} --> {self.comboBox_recipe.currentText()}')
except Exception as e:
self.dev_logger.error(f'Recipe_Change {self.MYINI.recipe} --> {self.comboBox_recipe.currentText()}\n{e}')
def softwareTest(self):
self.dbwindow.reset_data()
self.dbwindow.creat_Serial_Number()
self.dbwindow.aoi_start = datetime.now()
self.screwdrive = ScrewDrive(r".\model")
self.screwdrive.report.connect(self.NG_Report)
self.screwdrive.status.connect(self.statusChange)
self.label_OK.setText('calculating')
self.All_AIPass = True
self.All_Report_count = 0
self.inputimage = copy.deepcopy(cv2.imread(r"D:\Screwdriver_Data\1211_60Boxes\AI\origin\AI_20231211_193650.png"))
self.Result = copy.deepcopy(self.inputimage)
self.screwdrive.Result = self.Result
self.screwdrive.image_AI = cv2.cvtColor(self.Result, cv2.COLOR_BGR2GRAY)
self.screwdrive.Main(1, self.dbwindow.aoi_start)
self.screwdrive.image_AOI = cv2.cvtColor(copy.deepcopy(cv2.imread(r"D:\Screwdriver_Data\1211_60Boxes\AI\origin\AI_20231211_193650.png")),
cv2.COLOR_BGR2GRAY)
self.screwdrive.AOIimage_ISREADY = True
def statusChange(self, statusNow):
# if self.All_AIPass or self.checkBox_saveImage.isChecked():
# output_file_name = f'{self.camera.cam.ExposureTime.GetValue()}.png'
# output_file_path = fr"D:\ScrewdriverFile\ScrewdriverImage\0922\{output_file_name}"
# cv2.imwrite(output_file_path, self.inputimage)
if statusNow:
self.Result = cv2.rotate(self.Result, cv2.ROTATE_90_CLOCKWISE)
self.imageLable_Show(self.label_Image, self.Result)
self.dbwindow.aoi_end = datetime.now()
self.dbwindow.write_data()
print(f'********************結束********************')
def NG_Report(self, is_OK, confidence, Index):
try:
self.All_Report(is_OK, confidence, Index)
# self.imageLable_Show(self.label_image_Result, self.Result)
if is_OK == 0:#AI False
self.All_AIPass = False
except Exception as e:
print(e)
def All_Report(self, is_OK, confidence, Index):
if not(is_OK in [1, 404]):
if self.dbwindow.ng_bits_index == '':
self.dbwindow.ng_bits_index = self.All_Report_count + 1
else:
self.dbwindow.ng_bits_index = f'{self.dbwindow.ng_bits_index},{self.All_Report_count + 1}'
self.All_Report_count += 1
if self.All_Report_count == len(self.MYINI.Class_Name_All):
print(self.screwdrive.NG_Image_Information.values())
is_Fuck = len([x for x in self.screwdrive.NG_Image_Information.values() if 'slotted' in x])
print(f'is_Fuck = {is_Fuck}')
self.Start24_Count += 1
if self.dbwindow.ng_bits_index == '':
self.dbwindow.pass_or_failure = True
self.DetectOK()
self.label_OK.setText('OK')
self.label_OK.setStyleSheet(
'background-color: #4CAF50; color: white; border-radius: 5px; padding: 10px; font-size: 72px;')
elif is_Fuck > 8:
self.dbwindow.ng_bits_index = ''
self.dbwindow.pass_or_failure = True
self.DetectNG()
self.label_OK.setText('不計數')
self.label_OK.setStyleSheet(
'background-color: #4CAF50; color: white; border-radius: 5px; padding: 10px; font-size: 72px;')
else:
self.NG_Count += 1
self.label_OK.setText('NG')
self.label_OK.setStyleSheet(
'background-color: #FF5555; color: white; border-radius: 5px; padding: 10px; font-size: 72px;')
self.DetectNG()
print(f'NG : {self.NG_Count} / {self.Start24_Count}')
def Seach(self):
filename, _ = QFileDialog.getOpenFileName(self, 'Open Image', './0712Verify/ROI', 'Image Files(*.png *.jpg *.bmp *.jpeg)')
self.inputimage_filename = filename
if filename:
try:
self.inputimage = cv2.imdecode(np.fromfile(filename, dtype=np.uint8), -1)
self.imageLable_Show(self.label_image_Measure_InputImage, self.inputimage)
except BaseException as e:
print('openFiles:', e)
def Main(self):
self.tableWidget_information_correctrate.clear()
self.tableWidget_information.clear()
self.messages_Failure_count = 0
self.messages_Finish_count = 0
self.status_engineer = 0
self.screwDrive_for_test.engineer(rf"{self.comboBox_Validation_set.currentText()}", self.status_engineer, model_fileName=self.comboBox_models.currentText())
# self.screwdrive.engineer(r"C:\Users\K\Desktop\1201Screwdriver_backup\imageprocess\1208_NG\Milwaukee_AI", 0)
self.tableWidget_information_correctrate.setHorizontalHeaderLabels(["class_number", "Total", "succese", "failure", "correct_rate"])
def Test_Measure(self):
self.status_engineer = 2
self.screwdrive.engineer(r"C:\Users\user\Desktop\ScrewDriver\trainningset\111", self.status_engineer)#0116
# threading.Thread(target=self.screwdrive.engineer, args=(r"D:\Screwdriver_Data\KleinTool\aoi\ROI", self.status_engineer)).start()
# threading.Thread(target=self.screwdrive.engineer, args=(r"D:\Screwdriver_Data\KleinTool\aoi\ROI", self.status_engineer)).start()
# self.screwdrive.engineer(r"D:\Screwdriver_Data\imageprocess\imageprocess_01161720_BackUp\test", self.status_engineer)
# self.screwdrive.engineer(r"D:\Screwdriver_Data\ROI_Factory_AOI", self.status_engineer)
def show_measure(self, measure_dic):
def Find_string_locate(s):
# 使用正規表達式找出字串中的英文單字
matches = re.findall(r'[a-zA-Z]+', s)
# 如果找到英文單字,則找出該單字後面第二個數字
print(f'matches = {matches}')
if matches:
target_word = matches[0] # 假設只有一個英文單字
numbers_after_word = re.findall(rf'{target_word}_\d+', s)
print(f'numbers_after_word = {numbers_after_word}')
# 如果找到數字,則輸出第二個數字
if numbers_after_word:
numbers = re.findall(r'\d+', numbers_after_word[0])
print(f'numbers = {numbers}')
if len(numbers) >= 2:
result = int(numbers[1])
print(result)
return result
else:
print("找不到足夠的數字")
else:
print("找不到數字")
else:
print("找不到英文單字")
self.tableWidget_information.setRowCount(len(measure_dic))
self.tableWidget_information.setColumnCount(5)
row = 0
index = -1
for filename, classname_size, diameters, aspect_ratio, vertices in measure_dic:
item = QTableWidgetItem(filename)
# 使用正則表達式尋找數字
numbers = re.findall(r'\d+', filename)
# 返回符合條件的數字
nums = [number for number in numbers if int(number) >= 0]
# 20231213_175019_torxtamperproof_2_20_0.656_6.png
# ['20231213', '161325', '5', '16', '1', '038', '6']
# 20231219_225350_torxtamperproof_2_19_0.665_480.0_570.0.png
# ['20231219', '225350', '2', '19', '0', '665', '480', '0', '570', '0']
# AOI_20231211_193650.png_roi_15.png
# ['20231211', '194114', '20']
# print(nums)
if len(nums[-2]) == 6:
num = int(nums[-1])
else:
num = int(nums[3])
numbers = re.findall(r'\d+', classname_size)
nums = [number for number in numbers if int(number) >= 0]
if len(nums) > 0:
size = int(nums[0])
if not (row == 0) and not (index == num):
item.setBackground(QColor(29, 230, 181))
self.tableWidget_information.setItem(row, 0, item)
item = QTableWidgetItem(str(classname_size))
# if not (row == 0) and not (num+size == 22):
# item.setBackground(QColor(181, 230, 29))
self.tableWidget_information.setItem(row, 1, item)
item = QTableWidgetItem(f"{diameters}")
self.tableWidget_information.setItem(row, 2, item)
item = QTableWidgetItem(str(aspect_ratio).zfill(6))
self.tableWidget_information.setItem(row, 3, item)
item = QTableWidgetItem(str('{:.6f}'.format(round(vertices, 6))).zfill(10))
self.tableWidget_information.setItem(row, 4, item)
row += 1
index = num
def ShowImage(self, row, column):
try:
filenameitem = self.tableWidget_information.item(row, column)
if column == 0 and filenameitem is not None:
filename = filenameitem.text()
if self.status_engineer == 2:
self.imageLable_Show(self.label_image_information, self.screwdrive.Measure_dic_Image[filename][0])
self.imageLable_Show(self.label_image_histogram, self.screwdrive.Measure_dic_Image[filename][1])
self.imageLable_Show(self.label_image_Remedy, self.screwdrive.Measure_dic_Image[filename][2])
self.imageLable_Show(self.label_image_extend, self.screwdrive.Measure_dic_Image[filename][3])
self.Vertical_Projection_Show(self.screwdrive.Measure_dic_Image[filename][0])
elif self.status_engineer == 0:
self.imageLable_Show(self.label_image_information, self.screwDrive_for_test.ErrorImage[filename])
except Exception as e:
self.dev_logger.info(f'ShowImage\n{e}')
def Histogram_Show(self, image):
hist = cv2.calcHist([image], [0], None, [256], [0, 256])
# 設定直方圖的寬度和高度
scene = QGraphicsScene(self.graphicsView_Measure)
self.graphicsView_Measure.setScene(scene)
bar_width = 2.5
max_height = max(hist[1:])
# print(len(hist[1:]))
# 繪製每個條形
for i, value in enumerate(hist):
bar_height = (value / max_height) * 400
bar = QGraphicsRectItem(i * (bar_width + 1), 400 - bar_height, bar_width, bar_height)
# 設定條形的顏色
if i % 5 == 0:
bar.setBrush(QColor(Qt.red))
else:
bar.setBrush(QColor(Qt.blue))
# 將條形添加到場景中
scene.addItem(bar)
def Vertical_Projection_Show(self, image):
# ret, image = cv2.threshold(image, 200, 255, cv2.THRESH_BINARY)
vertical_projection = np.sum(image, axis=0)
# print(vertical_projection)
# print([x for x, k in enumerate(np.sum(image, axis=0)) if k > 0])
# 設定直方圖的寬度和高度
scene = QGraphicsScene(self.graphicsView_Measure)
self.graphicsView_Measure.setScene(scene)
bar_width = 2.5
max_height = max(vertical_projection[1:])/255
# print(len(hist[1:]))
# 繪製每個條形
for i, value in enumerate(vertical_projection):
bar_height = (value/255 / max_height) * 400
bar = QGraphicsRectItem(i * (bar_width + 1), 400 - bar_height, bar_width, bar_height)
# 設定條形的顏色
if i % 5 == 0:
bar.setBrush(QColor(Qt.red))
else:
bar.setBrush(QColor(Qt.blue))
# 將條形添加到場景中
scene.addItem(bar)
def imageLable_Show(self, image_label, Show_image):
if len(Show_image.shape) == 3:
Show_image = cv2.cvtColor(Show_image, cv2.COLOR_BGR2RGB) # 圖像存儲使用8-8-8 24位RGB格式
else:
Show_image = cv2.cvtColor(Show_image, cv2.COLOR_GRAY2RGB) # 圖像存儲使用8-8-8 24位RGB格式
rows, cols, channels = Show_image.shape # 獲取圖片長寬
QImg = QImage(Show_image.data, cols, rows, cols*channels, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(QImg)
image_label.setPixmap(pixmap)
def Measure(self):
# inputimage = cv2.imread('./VarificationSet0708_model0708batchsize12epoch6/04_Hex/2023-07-08_19-26-09 2.png')
# 取得目前檔案的絕對路徑
file_path = os.path.abspath(self.inputimage_filename)
# 取得檔案所在的資料夾路徑
folder_path = os.path.dirname(file_path)
# 取得資料夾名稱
folder_name = os.path.basename(folder_path)
image, _, _, _ = Measure(self.inputimage).measure(folder_name)
self.imageLable_Show(self.label_image_Measure_ROI, image)
cv2.imwrite('./Measure_result.png', image)
def receive_message_Finish(self, class_number, Total, succese, failure, correct_rate):
item = QTableWidgetItem(class_number)
self.tableWidget_information_correctrate.setItem(self.messages_Finish_count, 0, item)
item = QTableWidgetItem(Total)
self.tableWidget_information_correctrate.setItem(self.messages_Finish_count, 1, item)
item = QTableWidgetItem(succese)
self.tableWidget_information_correctrate.setItem(self.messages_Finish_count, 2, item)
item = QTableWidgetItem(failure)
self.tableWidget_information_correctrate.setItem(self.messages_Finish_count, 3, item)
item = QTableWidgetItem(correct_rate)
self.tableWidget_information_correctrate.setItem(self.messages_Finish_count, 4, item)
self.messages_Finish_count += 1
def receive_message(self, img_filename, file, class_number, confidence):
# print(u'正在閱讀新文章內容:%s' % event.dict["artical"])
item = QTableWidgetItem(img_filename)
self.tableWidget_information.setItem(self.messages_Failure_count, 0, item)
item = QTableWidgetItem(file)
self.tableWidget_information.setItem(self.messages_Failure_count, 1, item)
item = QTableWidgetItem(class_number)
self.tableWidget_information.setItem(self.messages_Failure_count, 2, item)
item = QTableWidgetItem(confidence)
self.tableWidget_information.setItem(self.messages_Failure_count, 3, item)
self.messages_Failure_count += 1
self.tableWidget_information.setRowCount(self.messages_Failure_count+1)
def AI_Class_Changed(self):
try:
self.show_measure(self.screwdrive.Measure_dic[self.comboBox_AI_Class.currentText()])
self.dev_logger.info(f'AI_Class_Changed Change to --> {self.comboBox_AI_Class.currentText()}')
except Exception as e:
self.dev_logger.info(f'AI_Class_Changed Change to --> {self.comboBox_AI_Class.currentText()}\n{e}')
def measure_step(self, class_name):
self.comboBox_AI_Class.addItem(class_name)
from tensorflow.keras import models
from tensorflow.keras.preprocessing.image import img_to_array
class ScrewDrive_For_Test(QThread):
trigger = pyqtSignal(str, str, str, str)
finish = pyqtSignal(str, str, str, str, str)
output_finish = pyqtSignal(str, str, str, str)
def __init__(self, identity='apha', myIni=None):
super().__init__()
self.Identity = identity
self.Identity_CodeName = ['alpha', 'beta', 'gamma', 'delta']
# with tf.device('/cpu:0'):
self.Myini = myIni
self.ErrorImage = {}
self.Input_FilePath = ''
self.modecode = -1
def split_digits_in_img(self, img_array):
x_list = list()
# for i in range(digits_in_img):
x_list.append(img_array)
return x_list
def engineer(self, filepath, modecode, model_fileName=None):
self.modecode = modecode
self.Input_FilePath = filepath
self.Test_noimread_Model_FileName = model_fileName
self.start()
def Test_noimread(self):
# print(f'loaded madel : checkpoint\{self.Test_noimread_Model_FileName}')
Model = models.load_model(fr'D:\ScrewdriverFile\model\0509\13\{self.Test_noimread_Model_FileName}')
np.set_printoptions(suppress=True, linewidth=150, precision=9, formatter={'float': '{: 0.9f}'.format})
image_list = list()
self.Input_FilePath = fr'D:\ScrewdriverFile\ScrewdriverImage\Varification\{self.Input_FilePath}'
file_list = os.listdir(self.Input_FilePath)
for k, file in enumerate(file_list):
img_filenames = os.listdir(fr'{self.Input_FilePath}/{file}')
for img_filename in img_filenames:
InputImage = cv2.imread(fr'{self.Input_FilePath}/{file}/{img_filename}', cv2.IMREAD_GRAYSCALE)
image_list.append(image_k_file_img(k, file, img_filename, InputImage))
start = time.time()
class_list_correct_rate = list()
succese = 0
failure = 0
class_end = False
class_start = -1
imgs_array = [
np.expand_dims(img_to_array(cv2.resize(img.Image, (112, 112), interpolation=cv2.INTER_CUBIC)), axis=0) for
img in image_list]
x = np.concatenate([x for x in imgs_array])
if len(file_list) == 3:
print('small')
result_class = Model.predict(x, verbose=0)
else:
print('normal')
result_class = Model.predict(x, verbose=0)
print(f'result_class = {result_class}')
result_Max_Index = [np.argmax(r_c, axis=0) for r_c in result_class]
print(f'result_Max_Index = {result_Max_Index}')
class_names_AI = [file_list[int(Index)] for Index in result_Max_Index]
confidences = [str(result_class[i][Index]) for i, Index in enumerate(result_Max_Index)]
index_class = [index.Image_Index for index in image_list]
for i, image in enumerate(image_list):
if image.Image_Index is not class_start and class_end:
class_end = False
print('')
correct_rate = round(succese / (succese + failure), 3)
class_list_correct_rate.append(correct_rate)
self.send_message_Finish(
[image_list[i-1].file, str(succese + failure), str(succese), str(failure), str(correct_rate)])
print(
f'********************************************** 正確率 = {correct_rate} **********************************************')
if image.Image_Index is not class_start:
class_start = image.Image_Index
class_end = True
succese = 0
failure = 0
print(
f' ********************************************** file_list = {image.file} **********************************************')
if class_names_AI[i] == file_list[index_class[i]]:
succese += 1
else:
self.send_message([image.img_filename, image.file, class_names_AI[i], confidences[i]])
self.ErrorImage[image.img_filename] = image.Image
failure += 1
print('')
correct_rate = round(succese / (succese + failure), 3)
class_list_correct_rate.append(correct_rate)
self.send_message_Finish([image_list[i - 1].file, str(succese + failure), str(succese), str(failure), str(correct_rate)])
print(f'********************************************** 正確率 = {correct_rate} **********************************************')
for i in range(len(class_list_correct_rate)):
print(f'{file_list[i]} 的正確率 = {class_list_correct_rate[i]}')
end = time.time()
print(f' ------------------------------------ 耗時 --> {(end - start)} ------------------------------------')
def send_message(self, message):
# 事件物件,寫了新文章
self.trigger.emit(message[0], message[1], message[2], message[3])
def send_message_Finish(self, message):
# 事件物件,寫了新文章
self.finish.emit(message[0], message[1], message[2], message[3], message[4])
def run(self):
if self.modecode == 0:
self.Test_noimread()
class image_k_file_img:
def __init__(self, k, file, img_filename, img):
self.Image_Index = k
self.file = file
self.img_filename = img_filename
self.Image = img
if __name__ == '__main__':
app = QApplication(sys.argv) # 固定的PyQt5程式都需要QApplication對象。sys.argv是命令列參數清單確保程式可以按兩下運行
MyWindow = MainWindow() # 初始化
MyWindow.show() # 將視窗控制項顯示在螢幕上
sys.exit(app.exec_()) # 程式運行sys.exit方法確保程式完整退出