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

451 lines
23 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 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方法確保程式完整退出