import configparser import copy import threading import time from datetime import datetime import numpy as np import os import sys from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QFileDialog, QApplication, QTableWidgetItem, QHeaderView, QHBoxLayout import cv2 from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import QThread, pyqtSignal, QObject from ROI_Window.ROI_Window import Ui_ROI_Window from ScrewDrive import ScrewDrive from Camera import Camera class ROIWindow(QtWidgets.QMainWindow, Ui_ROI_Window): def __init__(self, public_camera=None, public_screwdrive=None, public_Motor=None, myini=None): QtWidgets.QMainWindow.__init__(self) # 創建主界面對象 self.setupUi(self) self.screwdrive = public_screwdrive self.motor = public_Motor self.Myini = myini self.label_image_Main.setScaledContents(True) self.label_image_Grab.setScaledContents(True) self.label_image_ForFoucs.setScaledContents(True) self.label_image_ROI.setScaledContents(True) self.comboBox_recipe.addItems([recipe for recipe in self.Myini.recipes]) self.screwdriver_roi = ScrewDriver_ROI(self.comboBox_recipe.currentText()) self.iniGuiEvent() config = configparser.ConfigParser() config.read('ScrewDrive.ini') self.label_OutputFilePath.setText(config['ROIInformation']["outputfilepath"]) self.camera = public_camera if self.camera.Camera_isOpen: self.comboBox_ExposureTime.addItems([str(exposuretime) for exposuretime in self.camera.ExposureTime_List]) self.comboBox_ForFoucs.addItems([Class_Name for Class_Name in self.Myini.Class_Name_All]) self.camera.FreeRun.connect(self.FreeRun) else: self.pushButton_cameralink.setEnabled(False) self.pushButton_cameralink.setText('相機未連線') self.pushButton_cameralink.setStyleSheet('color: red') self.pushButton_cameraGrab.setEnabled(False) self.pushButton_cameraGrab_ROI.setEnabled(False) self.pushButton_Preview.setEnabled(False) self.horizontalSlider_ExposureTime.setEnabled(False) self.comboBox_ExposureTime.setEnabled(False) def iniGuiEvent(self): self.pushButton_start.clicked.connect(self.ROI_WriteIni) self.comboBox_recipe.currentIndexChanged.connect(self.Recipe_Change) self.pushButton_changeoutputfilepath.clicked.connect(self.ChangeOutputFilePath) self.pushButton_cameralink.clicked.connect(self.CameraLink) self.pushButton_cameraGrab.clicked.connect(self.CameraGrab) self.pushButton_cameraGrab_ROI.clicked.connect(self.CameraGrab_ROI) self.pushButton_cameraGrab_ExpTime.clicked.connect(self.CameraGrab_ROI_ExpTime) self.pushButton_CreateExposureTime.clicked.connect(self.HaveNewExposureTime) self.horizontalSlider_ExposureTime.valueChanged.connect(self.WantToCreateExposureTime) self.comboBox_ExposureTime.currentIndexChanged.connect(self.ExposureTime_Change) self.pushButton_ReadImage_ROI.clicked.connect(self.screwdriver_roi.ReadImage_ROI) # self.ROI_X.valueChanged.connect(self.WantToCreateExposureTime) # self.ROI_Y.valueChanged.connect(self.WantToCreateExposureTime) self.pushButton_adjust_ROI.clicked.connect(self.adjust_ROI) self.pushButton_Preview.clicked.connect(self.Preview) self.comboBox_ForFoucs.currentIndexChanged.connect(self.ForFocus) self.pushButton_Next.clicked.connect(self.Next) self.pushButton_Previous.clicked.connect(self.Previous) self.pushButton_move.clicked.connect(self.motorGO) def ROI_for_Trainning(self): Pre_ROI_List = list() config = configparser.ConfigParser() config.read('ScrewDrive.ini') for key in config[f'Pre_ROI_KleinTool']: Pre_ROI_List.append([int(x) for x in config[f'Pre_ROI_KleinTool'][key].split(',')]) print(f'Pre_ROI_List = {Pre_ROI_List}') Input_FilePath = r'D:\ScrewdriverFile\ScrewdriverImage\KlienTools\Image_KleinTool_0201\opposite' img_filenames = os.listdir(Input_FilePath) for img_filename in img_filenames: print(img_filename) InputImage = cv2.flip(cv2.imread(fr'{Input_FilePath}/{img_filename}', cv2.IMREAD_COLOR), 1) for k, pre_rio in enumerate(Pre_ROI_List): threading.Thread(target=self.screwdriver_roi.ROI, args=(InputImage[pre_rio[1]:pre_rio[1]+pre_rio[3], pre_rio[0]:pre_rio[0]+pre_rio[2]], fr'{img_filename}_{k}',)).start() def closeEvent(self, event): print('ROI_Window was Closed') self.motorGO() self.Myini.read_ini() def Next(self): print(f"self.comboBox_ForFoucs.currentIndex() = {self.comboBox_ForFoucs.currentIndex()}") if self.comboBox_ForFoucs.currentIndex()+1 == len(self.Myini.Class_Name_All): self.comboBox_ForFoucs.setCurrentIndex(0) else: self.comboBox_ForFoucs.setCurrentIndex(self.comboBox_ForFoucs.currentIndex()+1) def Previous(self): print(f"self.comboBox_ForFoucs.currentIndex() = {self.comboBox_ForFoucs.currentIndex()}") if self.comboBox_ForFoucs.currentIndex()-1 < 0: self.comboBox_ForFoucs.setCurrentIndex(38) else: self.comboBox_ForFoucs.setCurrentIndex(self.comboBox_ForFoucs.currentIndex()-1) def motorGO(self): self.motor.Move() def Preview(self): self.imageLable_Show(self.label_image_ROI, self.screwdriver_roi.Preview(self.camera.Image_RealTime)) def adjust_ROI(self): config = configparser.ConfigParser() config.read('ScrewDrive.ini') if f'AIClass_{self.screwdriver_roi.Recipe}' in config and f'ROI_{self.screwdriver_roi.Recipe}' in config: key = '' if self.screwdriver_roi.Recipe == 'Milwaukee': roiSize = 100 else: roiSize = 150 if self.checkBox_All_adjust_ROI.isChecked(): for i, roi_data in enumerate(self.screwdriver_roi.ROI_List): X = roi_data.X + self.ROI_X.value() if roi_data.X + self.ROI_X.value() >= 0 else 0 X = roi_data.X + self.ROI_X.value() if roi_data.X + self.ROI_X.value() < self.camera.Width else self.camera.Width - 1 Y = roi_data.Y + self.ROI_Y.value() if roi_data.Y + self.ROI_Y.value() >= 0 else 0 Y = roi_data.Y + self.ROI_Y.value() if roi_data.Y + self.ROI_Y.value() < self.camera.Height else self.camera.Height - 1 config[f'ROI_{self.screwdriver_roi.Recipe}'][str(i)] = f'{X},{Y},{roiSize},{roiSize}' else: key = self.Myini.Class_Name_All.index(self.comboBox_ForFoucs.currentText()) roi_data = self.screwdriver_roi.ROI_List[key] X = roi_data.X + self.ROI_X.value() if roi_data.X + self.ROI_X.value() >= 0 else 0 X = roi_data.X + self.ROI_X.value() if roi_data.X + self.ROI_X.value() < self.camera.Width else self.camera.Width - 1 Y = roi_data.Y + self.ROI_Y.value() if roi_data.Y + self.ROI_Y.value() >= 0 else 0 Y = roi_data.Y + self.ROI_Y.value() if roi_data.Y + self.ROI_Y.value() < self.camera.Height else self.camera.Height - 1 config[f'ROI_{self.screwdriver_roi.Recipe}'][str(key)] = f'{X},{Y},{roiSize},{roiSize}' with open('ScrewDrive.ini', 'w') as conf: config.write(conf) self.screwdriver_roi.read_ini() self.ROI_X.setValue(0) self.ROI_Y.setValue(0) def ForFocus(self): self.ROI_X.setValue(0) self.ROI_Y.setValue(0) def ExposureTime_Change(self): if self.comboBox_ExposureTime.currentText() != '': self.camera.SetExposureTime(int(self.comboBox_ExposureTime.currentText())) def WantToCreateExposureTime(self): self.camera.SetExposureTime(float(self.horizontalSlider_ExposureTime.value())) self.label_ExposureTime.setText(str(self.horizontalSlider_ExposureTime.value())) def HaveNewExposureTime(self): self.camera.ExposureTime_List.append(self.horizontalSlider_ExposureTime.value()) def ExposureTime_toint(e): return int(e) self.camera.ExposureTime_List = sorted(self.camera.ExposureTime_List, key=ExposureTime_toint) config = configparser.ConfigParser() config.read('ScrewDrive.ini') for i, e in enumerate(self.camera.ExposureTime_List): config.set('ExposureTime', str(i+1), str(e)) config.write(open('ScrewDrive.ini', "r+")) # r+模式 self.comboBox_ExposureTime.clear() self.comboBox_ExposureTime.addItems([str(exposuretime) for exposuretime in self.camera.ExposureTime_List]) def CameraLink(self): self.camera.start() def CameraGrab(self): image = self.camera.Image_RealTime loc_dt = datetime.today() image_name = loc_dt.strftime("%Y_%m_%d_%H_%M_%S") self.makexml.Create_XML_ForGrab(image_name) image = self.screwdriver_roi.ROI(image, image_name) self.imageLable_Show(self.label_image_Grab, image) def CameraGrab_ROI_ExpTime(self): exposure_times = [int(self.comboBox_ExposureTime.itemText(i)) for i in range(self.comboBox_ExposureTime.count())] for exposure_time in exposure_times: self.camera.SetExposureTime(exposure_time) time.sleep(1) image = self.camera.Image_RealTime image = self.screwdriver_roi.ROI(image) self.imageLable_Show(self.label_image_Grab, image) def CameraGrab_ROI(self): current_datetime = datetime.now() formatted_datetime = current_datetime.strftime('%Y_%m_%d_%H_%M_%S') output_file_name = f'{self.comboBox_ForFoucs.currentText()}_{formatted_datetime}.png' output_file_path = os.path.join(self.screwdriver_roi.OutputFilePath, output_file_name) cv2.imwrite(output_file_path, self.roi) def FreeRun(self): image = self.camera.Image_RealTime self.imageLable_Show(self.label_image_Main, image) image = cv2.flip(image, 1) roi_data = self.screwdriver_roi.ROI_List[ self.Myini.Class_Name_All.index(self.comboBox_ForFoucs.currentText())] X = roi_data.X + self.ROI_X.value() if roi_data.X + self.ROI_X.value() >= 0 else 0 X = roi_data.X + self.ROI_X.value() if roi_data.X + self.ROI_X.value() < self.camera.Width else self.camera.Width - 1 Y = roi_data.Y + self.ROI_Y.value() if roi_data.Y + self.ROI_Y.value() >= 0 else 0 # print(f"roi_data.Y = {roi_data.Y}") Y = roi_data.Y + self.ROI_Y.value() if roi_data.Y + self.ROI_Y.value() < self.camera.Height else self.camera.Height - 1 self.roi = image[Y:Y + roi_data.H, X:X + roi_data.W] self.imageLable_Show(self.label_image_ForFoucs, self.roi) def ChangeOutputFilePath(self): dir = QFileDialog.getExistingDirectory(self, "Open Directory", './', QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) self.label_OutputFilePath.setText(dir) config = configparser.ConfigParser() config.read('ScrewDrive.ini') config.set('ROIInformation', "outputfilepath", dir) config.write(open('ScrewDrive.ini', "r+")) # r+模式 self.screwdriver_roi.OutputFilePath = dir def Recipe_Change(self): self.screwdriver_roi.recipe_change(self.comboBox_recipe.currentText()) self.Myini.recipe_change(self.comboBox_recipe.currentText()) self.comboBox_ForFoucs.clear() self.comboBox_ForFoucs.addItems([Class_Name for Class_Name in self.Myini.Class_Name_All]) # image = cv2.imread(f'test/{self.screwdriver_roi.Recipe}.bmp') # image = cv2.flip(image, 1) # for i, roi_data in enumerate(self.screwdriver_roi.ROI_List): # cv2.rectangle(image, (roi_data.X, roi_data.Y), (roi_data.X + roi_data.W, roi_data.Y + roi_data.H), # (0, 255, 0), 2) # self.imageLable_Show(self.label_image_ROI, image) def ROI_WriteIni(self): image = self.camera.Image_RealTime image = cv2.flip(image, 1) # Milwakee_tuple = ('Milwakee', 39, 410, 150, 225, 260, 110, 40) # Milwaukee_tuple = ('Milwaukee', 39, 280, 400, 259, 225, 150, 0, 0) # Y軸鏡像 Milwaukee_tuple = ('Milwaukee', 39, 302, 416, 259, 225, 110, 0, -1) # Y軸鏡像 PlatinumTools_tuple = ('PlatinumTools', 30, 768, 355, 180, 215, 150, 2, -1) # ThreeCrossThree_tuple = ('ThreeCrossThree', 9, 1410, 747, 165, 170, 150, 2, -1) RingSix_tuple = ('RingSix', 6, 0, 0, 0, 0, 150, 0, 0) # 手動 KleinTool_tuple = ('KleinTool', 32, 1343, 348, 150, 153, 150, 0, 2) PhillipsGroup_tuple = ('PhillipsGroup', 10, 0, 0, 0, 0, 150, 0, 0) PozidrivGroup_tuple = ('PozidrivGroup', 10, 0, 0, 0, 0, 150, 0, 0) TorxGroup_tuple = ('TorxGroup', 10, 770, 392, 160, 0, 150, 1, 0) Buffalo_tuple = ('Buffalo', 10, 1590, 208, 0, 150, 150, 0, -2) screwdriverTypeTuple = Milwaukee_tuple firstPoint_X = screwdriverTypeTuple[2] firstPoint_Y = screwdriverTypeTuple[3] gapX = screwdriverTypeTuple[4] gapY = screwdriverTypeTuple[5] ROI_Size = screwdriverTypeTuple[6] ROI_bais_X = screwdriverTypeTuple[7] ROI_bais_Y = screwdriverTypeTuple[8] y = 0 x = 0 w = 0 h = 0 count = 0 config = configparser.ConfigParser() config[self.comboBox_recipe.currentText()] = {} if (screwdriverTypeTuple[0] == 'Milwaukee'): for i in range(0, 4): for j in range(0, 12 if i < 3 else 3): x = firstPoint_X + j * gapX + j * ROI_bais_X y = firstPoint_Y + i * gapY + i * ROI_bais_Y w = h = ROI_Size cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2) config[self.comboBox_recipe.currentText()][str(count)] = f'{x},{y},{w},{h}' count += 1 elif (screwdriverTypeTuple[0] == 'PlatinumTools'): for i in range(0, 3): for j in range(0, 10): x = firstPoint_X + j * gapX + j * ROI_bais_X y = firstPoint_Y + i * gapY + i * ROI_bais_Y w = h = ROI_Size cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2) config[self.comboBox_recipe.currentText()][str(count)] = f'{x},{y},{w},{h}' count += 1 elif (screwdriverTypeTuple[0] == 'ThreeCrossThree'): for i in range(0, 3): for j in range(0, 3): x = firstPoint_X + j * gapX + j * ROI_bais_X y = firstPoint_Y + i * gapY + i * ROI_bais_Y w = h = ROI_Size cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 1) config[self.comboBox_recipe.currentText()][str(count)] = f'{x},{y},{w},{h}' count += 1 elif (screwdriverTypeTuple[0] == 'KleinTool'): for i in range(0, 8): for j in range(0, 4): x = firstPoint_X + j * gapX + j * ROI_bais_X y = firstPoint_Y + i * gapY + i * ROI_bais_Y w = h = ROI_Size cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 1) config[self.comboBox_recipe.currentText()][str(count)] = f'{x},{y},{w},{h}' count += 1 elif (screwdriverTypeTuple[0] == 'TorxGroup'): for i in range(0, 1): for j in range(0, 10): x = firstPoint_X + j * gapX + j * ROI_bais_X y = firstPoint_Y + i * gapY + i * ROI_bais_Y w = h = ROI_Size cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 1) config[self.comboBox_recipe.currentText()][str(count)] = f'{x},{y},{w},{h}' count += 1 elif (screwdriverTypeTuple[0] == 'Buffalo'): for i in range(0, 10): for j in range(0, 1): x = firstPoint_X + j * gapX + j * ROI_bais_X y = firstPoint_Y + i * gapY + i * ROI_bais_Y w = h = ROI_Size cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 1) config[self.comboBox_recipe.currentText()][str(count)] = f'{x},{y},{w},{h}' count += 1 with open('ROI.ini', 'w') as f: config.write(f) self.imageLable_Show(self.label_image_ROI, image) 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.imread(filename, cv2.IMREAD_COLOR) self.inputimage = self.rotate_and_mirror(self.inputimage) self.imageLable_Show(self.label_image_Main, self.inputimage) except BaseException as e: print('openFiles:', e) def imageLable_Show(self, image_label, Show_image): Show_image = cv2.cvtColor(Show_image, cv2.COLOR_BGR2RGB) # 圖像存儲使用8-8-8 24位RGB格式 rows, cols, channels = Show_image.shape # 獲取圖片長寬 image_label.geometry().width = cols image_label.geometry().height = rows QImg = QImage(Show_image.data, cols, rows, cols*channels, QImage.Format_RGB888) pixmap = QPixmap.fromImage(QImg) image_label.setPixmap(pixmap) class ScrewDriver_ROI(QThread): def __init__(self, recipe): super().__init__() self.Recipe = recipe self.ROI_List = list() self.OutputFilePath = '' self.read_ini() def read_ini(self): config = configparser.ConfigParser() config.read('ScrewDrive.ini') self.OutputFilePath = config['ROIInformation']['outputfilepath'] print(f'AIClass_{self.Recipe}') print(f'ROI_{self.Recipe}') if f'AIClass_{self.Recipe}' in config and f'ROI_{self.Recipe}' in config: self.ROI_List.clear() ROIData_List = list(zip(config[f'AIClass_{self.Recipe}'], config[f'ROI_{self.Recipe}'])) for key_type, key_XYWH in ROIData_List: number_list = config[f'ROI_{self.Recipe}'][key_XYWH].split(',') print(f'number_list = {number_list}') self.ROI_List.append( ROI_Data(int(number_list[0]), int(number_list[1]), int(number_list[2]), int(number_list[3]), config[f'AIClass_{self.Recipe}'][key_type])) print(f'len(self.ROI_List) = {len(self.ROI_List)}') def recipe_change(self, recipe): self.Recipe = recipe self.read_ini() print('recipe_change') def ReadImage_ROI(self): if self.Recipe == 'Milwaukee': try: inpup_file = r'D:\ScrewdriverFile\ScrewdriverImage\0509_verify\AOI' img_filenames = os.listdir(inpup_file) for img_filename in img_filenames: ImageSRC = cv2.imread(f'{inpup_file}/{img_filename}') ImageSRC = cv2.flip(ImageSRC, 1) result = self.ROI(ImageSRC, img_filename) cv2.imwrite(os.path.join(f'{self.OutputFilePath}/origin', img_filename), result) except Exception as e: print(f'ReadImage_ROI Error\n{e}') elif self.Recipe == 'KleinTool': Pre_ROI_List = list() config = configparser.ConfigParser() config.read('ScrewDrive.ini') for key in config[f'Pre_ROI_KleinTool']: Pre_ROI_List.append([int(x) for x in config[f'Pre_ROI_KleinTool'][key].split(',')]) Input_FilePath = r'D:\Screwdriver_Data\Image_KleinTool_0201\AOI' img_filenames = os.listdir(Input_FilePath) for img_filename in img_filenames: print(img_filename) InputImage = cv2.flip(cv2.imread(fr'{Input_FilePath}/{img_filename}', cv2.IMREAD_COLOR), 1) for k, pre_rio in enumerate(Pre_ROI_List): threading.Thread(target=self.ROI, args=( InputImage[pre_rio[1]:pre_rio[1] + pre_rio[3], pre_rio[0]:pre_rio[0] + pre_rio[2]], fr'{img_filename}_{k}',)).start() def ROI(self, ImageSRC, image_name=None): if image_name == None: loc_dt = datetime.today() image_name = loc_dt.strftime("%Y_%m_%d_%H_%M_%S") os.makedirs(f'{self.OutputFilePath}/origin', exist_ok=True) cv2.imwrite(os.path.join(f'{self.OutputFilePath}/origin', f'{image_name}.png'), cv2.flip(ImageSRC, 1)) # 進行順時針旋轉90度 # rotated_img = cv2.rotate(ImageSRC, cv2.ROTATE_90_CLOCKWISE) # 沿著Y軸鏡射 mirrored_img = cv2.flip(ImageSRC, 1) ImageSRC_Gray = cv2.cvtColor(mirrored_img, cv2.COLOR_BGR2GRAY) roi_image = copy.deepcopy(ImageSRC_Gray) for i, rd in enumerate(self.ROI_List): roi_data = copy.deepcopy(rd) roi_data.X = roi_data.X#-15 roi_data.Y = roi_data.Y#-15 roi_data.W = roi_data.W#+30 roi_data.H = roi_data.H#+30 cv2.rectangle(mirrored_img, (roi_data.X, roi_data.Y), (roi_data.X + roi_data.W, roi_data.Y + roi_data.H), (0, 255, 0), 2) roi = roi_image[roi_data.Y:roi_data.Y+roi_data.H, roi_data.X:roi_data.X+roi_data.W] filename = f'{image_name}_roi_{i}.png' os.makedirs(f'{self.OutputFilePath}/ROI/{roi_data.Type}', exist_ok=True) cv2.imwrite(os.path.join(f'{self.OutputFilePath}/ROI/{roi_data.Type}', filename), roi) # cv2.imwrite(os.path.join(f'{self.OutputFilePath}/origin', f'{image_name}_mirrored_img.png'), mirrored_img) return mirrored_img def CameraGrab_ROI_ExpTime(self, ImageSRC, image_name=None): image = self.camera.Image_RealTime image = self.screwdriver_roi.ROI(image) self.imageLable_Show(self.label_image_Grab, image) def Preview(self, ImageSRC): mirrored_img = cv2.flip(ImageSRC, 1) for i, roi_data in enumerate(self.ROI_List): cv2.rectangle(mirrored_img, (roi_data.X, roi_data.Y), (roi_data.X + roi_data.W, roi_data.Y + roi_data.H), (255, 255, 0), 2) return mirrored_img class ROI_Data: def __init__(self, x=0, y=0, w=0, h=0, type=''): self.X = x self.Y = y self.W = w self.H = h self.Type = type if __name__ == '__main__': app = QApplication(sys.argv) # 固定的,PyQt5程式都需要QApplication對象。sys.argv是命令列參數清單,確保程式可以按兩下運行 MyWindow = ROIWindow() # 初始化 MyWindow.show() # 將視窗控制項顯示在螢幕上 sys.exit(app.exec_()) # 程式運行,sys.exit方法確保程式完整退出