1214 lines
65 KiB
Python
1214 lines
65 KiB
Python
import configparser
|
||
import copy
|
||
import glob
|
||
import logging
|
||
import math
|
||
import re
|
||
import threading
|
||
from datetime import datetime
|
||
import pandas as pd
|
||
|
||
import numpy as np
|
||
import os
|
||
import sys
|
||
import csv
|
||
from ultralytics import YOLO
|
||
import cv2
|
||
import matplotlib.pyplot as plt
|
||
from PyQt5.QtCore import QThread, pyqtSignal, QObject
|
||
import time
|
||
# import imutils
|
||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||
|
||
class ScrewDrive(QThread):
|
||
report = pyqtSignal(int, str, int, str, str)
|
||
trigger = pyqtSignal(str, str, str, str)
|
||
finish = pyqtSignal(str, str, str, str, str)
|
||
output_finish = pyqtSignal(str, str, str, str)
|
||
status = pyqtSignal(bool)
|
||
Measure_Step = pyqtSignal(str)
|
||
|
||
def __init__(self, model_filepath=None, dev_logger=None, identity='apha', myIni=None):
|
||
super().__init__()
|
||
|
||
self.Identity = identity
|
||
self.Identity_CodeName = ['alpha', 'beta', 'gamma', 'delta']
|
||
print(f'Identity = {self.Identity}')
|
||
# with tf.device('/cpu:0'):
|
||
self.Myini = myIni
|
||
|
||
if dev_logger == None:
|
||
self.iniLogging()
|
||
self.dev_logger.info('Program Start')
|
||
else:
|
||
self.dev_logger = dev_logger
|
||
|
||
if model_filepath == None:
|
||
print('Only Measure !!')
|
||
else:
|
||
self.models = list()
|
||
|
||
self.models.append(YOLO(f'{model_filepath}/{self.Myini.recipe_model}.pt'))
|
||
print(f'loaded madel : {model_filepath}/{self.Myini.recipe_model}.pt')
|
||
|
||
# self.models.append(models.load_model(f'{model_filepath}/{self.Myini.recipe_model}.h5'))
|
||
# print(f'loaded madel : {model_filepath}/{self.Myini.recipe_model}.h5')
|
||
|
||
# np.set_printoptions(suppress=True, linewidth=150, precision=9, formatter={'float': '{: 0.9f}'.format})
|
||
threading.Thread(target=self.First, args=()).start()
|
||
|
||
|
||
self.input_image_list = list()
|
||
self.ErrorImage = {}
|
||
self.image_AI = []
|
||
self.image_AOI = []
|
||
self.Result = []
|
||
self.Input_FilePath = ''
|
||
self.modecode = -1
|
||
self.AOIimage_ISREADY = False
|
||
self.lock = threading.Lock()
|
||
self.average_gray_values = list()
|
||
self.Predict_Result = list() # Output of self.Predict_Image(img, i), Input to AOI and Report
|
||
# self.read_ini()
|
||
|
||
self.Measure_dic = {}
|
||
self.Measure_dic_Image = {}
|
||
self.NG_Image_Information = {}
|
||
# print(self.Measuresize('pentalope', cv2.imread(r".\1202AOI_exp36000\ROI\11_Pentalope\2023_12_02_13_41_34_roi_28.png", cv2.IMREAD_COLOR), 28))
|
||
img_filenames = os.listdir(f'./imageprocess/{self.Myini.recipe}/AI')
|
||
self.Filenames_for_Record = list()
|
||
for img_filename in img_filenames:
|
||
self.Filenames_for_Record.append(img_filename)
|
||
def First(self):
|
||
image = cv2.imread(
|
||
r".\First\First.png",
|
||
cv2.IMREAD_GRAYSCALE)
|
||
self.models[0](image)
|
||
|
||
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)
|
||
os.makedirs(rf'./Logging/', exist_ok=True)
|
||
|
||
file_handler: logging.FileHandler = logging.FileHandler(rf'.\Logging\{logging_FileName}_dev.log', mode='a')
|
||
file_handler.setFormatter(formatter)
|
||
self.dev_logger.addHandler(file_handler)
|
||
def Main(self,modecode, time):
|
||
print(f'Main modecode = {modecode}')
|
||
self.Time_for_Record = time.strftime('%Y%m%d_%H%M%S')
|
||
self.modecode = modecode
|
||
self.start()
|
||
|
||
def engineer(self, filepath, modecode):
|
||
self.modecode = modecode
|
||
self.Input_FilePath = filepath
|
||
self.start()
|
||
|
||
def run(self):
|
||
if self.modecode == 0:
|
||
self.Test_noimread()
|
||
elif self.modecode == 2:
|
||
os.makedirs(f'./Measure_CSV', exist_ok=True)
|
||
os.makedirs(f'./Measure', exist_ok=True)
|
||
self.Test_Measure()
|
||
elif self.modecode == 1:
|
||
self.To_Run()
|
||
|
||
def To_Run(self):
|
||
# print(f'self.Pre_ROI_Dic = {self.Myini.Pre_ROI_Dic}')
|
||
print(f'self.Identity = {self.Identity}')
|
||
# print(f'self.ROI_List = {self.Myini.ROI_List}')
|
||
self.image_AI = self.image_AI[self.Myini.Pre_ROI_Dic[self.Identity][1]:self.Myini.Pre_ROI_Dic[self.Identity][1] + self.Myini.Pre_ROI_Dic[self.Identity][3], self.Myini.Pre_ROI_Dic[self.Identity][0]:self.Myini.Pre_ROI_Dic[self.Identity][0] + self.Myini.Pre_ROI_Dic[self.Identity][2]]
|
||
self.Result = self.Result[self.Myini.Pre_ROI_Dic[self.Identity][1]:self.Myini.Pre_ROI_Dic[self.Identity][1] + self.Myini.Pre_ROI_Dic[self.Identity][3], self.Myini.Pre_ROI_Dic[self.Identity][0]:self.Myini.Pre_ROI_Dic[self.Identity][0] + self.Myini.Pre_ROI_Dic[self.Identity][2]]
|
||
# print(f'self.Result.shape = {self.Result.shape}')
|
||
|
||
self.AOIimage_ISREADY = False
|
||
starttime = time.time()
|
||
|
||
Images_AI = self.ROI(self.image_AI)
|
||
class_names_AI, confidences, Index = self.Predict_Images_YOLOv8(Images_AI, 0)
|
||
# print(f'class_names_AI = {class_names_AI}')
|
||
|
||
endtime = time.time()
|
||
print(f'!!推論時間 : {endtime - starttime}')
|
||
|
||
# start = time.time()
|
||
# end_ROI = time.time()
|
||
# self.parallel_processing_AllImage(self.input_image_list, 0, self.lock)
|
||
# threads = list()
|
||
# for i, sl in enumerate(split_list(self.input_image_list, wanted_parts=len(self.models))):
|
||
try:
|
||
self.parallel_processing_AllImage(class_names_AI, Index, confidences, 0, self.lock, Images_AI)
|
||
except Exception as e:
|
||
self.dev_logger.error(f'parallel_processing_AllImage\n{e}')
|
||
|
||
self.status.emit(True)
|
||
|
||
def split_list(alist, wanted_parts=1):
|
||
length = len(alist)
|
||
return [alist[i * length // wanted_parts: (i + 1) * length // wanted_parts] for i in range(wanted_parts)]
|
||
|
||
def DrawResult(self, ROI_List_index, b, g, r):
|
||
# print(f'{self.Identity} DrawResult')
|
||
self.lock.acquire()
|
||
cv2.rectangle(self.Result,
|
||
(self.Myini.ROI_List[ROI_List_index].X_start, self.Myini.ROI_List[ROI_List_index].Y_start),
|
||
(self.Myini.ROI_List[ROI_List_index].X_stop, self.Myini.ROI_List[ROI_List_index].Y_stop),
|
||
(b, g, r), 3)
|
||
self.lock.release()
|
||
def parallel_processing_AllImage(self, class_names_AI, Index, confidences, models_index, lock, images_ai): #同時
|
||
while self.AOIimage_ISREADY == False:
|
||
time.sleep(0.01)
|
||
print('.')
|
||
self.image_AOI = self.image_AOI[self.Myini.Pre_ROI_Dic[self.Identity][1]:self.Myini.Pre_ROI_Dic[self.Identity][1] + self.Myini.Pre_ROI_Dic[self.Identity][3], self.Myini.Pre_ROI_Dic[self.Identity][0]:self.Myini.Pre_ROI_Dic[self.Identity][0] + self.Myini.Pre_ROI_Dic[self.Identity][2]]
|
||
# cv2.imwrite(f'./imageprocess/{self.Identity}.png', self.image_AOI)
|
||
|
||
AOI_Time = time.time()
|
||
imgs = self.ROI(self.image_AOI)
|
||
for img in imgs:
|
||
is_NG = False
|
||
is_NotValid = False
|
||
i = img.Image_Index
|
||
|
||
# diameter = '999'
|
||
# aspect_ratio = '999'
|
||
# numberofvertices = '999'
|
||
# image_measure = img.Image
|
||
|
||
try:
|
||
class_name, image_measure, diameter, aspect_ratio, numberofvertices = self.Measuresize(class_names_AI[Index.index(i)], img.Image, i)
|
||
except Exception as e:
|
||
diameter = 0
|
||
numberofvertices = 0
|
||
class_name = f'{class_names_AI[Index.index(i)]}_0'
|
||
self.dev_logger.error(f'Measuresize\n{e}')
|
||
# class_name = class_names_AI[Index.index(i)]
|
||
|
||
print(f'{self.Myini.Class_Name_All[i]} --> {class_name} index = {Index.index(i)}')
|
||
self.NG_Image_Information[self.Myini.Class_Name_All[i]] = class_name
|
||
if class_name == self.Myini.Class_Name_All[i]:
|
||
self.report.emit(1, confidences[Index.index(i)], i, diameter, numberofvertices)
|
||
self.DrawResult(i, 0, 255, 0)
|
||
# print(f'成功 第{i}張 {self.Myini.Class_Name_All[i]}')
|
||
elif 'NotValid' in class_name:
|
||
is_NotValid = True
|
||
cv2.imwrite(f'./imageprocess/{self.Myini.recipe}/NotValid/{self.Time_for_Record}_{class_name}_{i}_{diameter}_{numberofvertices}_{aspect_ratio}.png',image_measure)
|
||
cv2.imwrite(f'./imageprocess/{self.Myini.recipe}/NotValid_Origin/{self.Time_for_Record}_{class_name}_{i}_{diameter}_{numberofvertices}_{aspect_ratio}.png',img.Image)
|
||
self.report.emit(404, confidences[Index.index(i)], i, diameter, numberofvertices)
|
||
self.send_output_message_Finish(
|
||
[f'第{i}張', str(self.Myini.Class_Name_All[i]), str('NotValid'), str(class_name)])
|
||
self.DrawResult(i, 0, 0, 255)
|
||
# elif class_name in ['vacancy', 'opposite']:
|
||
# print(f'{class_name}進來了')
|
||
# self.send_output_message_Finish(
|
||
# [f'第{i}張', str(self.Myini.Class_Name_All[i]), str('無放插件'), 'Vacancy'])
|
||
# self.report.emit(10, 'Vacancy', i, diameter, numberofvertices)
|
||
# self.DrawResult(i, 255, 0, 0)
|
||
elif class_name in ['vacancy']:
|
||
print(f'{class_name}進來了')
|
||
self.send_output_message_Finish(
|
||
[f'第{i}張', str(self.Myini.Class_Name_All[i]), str('無放插件'), 'Vacancy'])
|
||
self.report.emit(10, 'Vacancy', i, diameter, numberofvertices)
|
||
self.DrawResult(i, 255, 0, 0)
|
||
elif class_name in ['opposite']:
|
||
print(f'{class_name}進來了')
|
||
self.send_output_message_Finish(
|
||
[f'第{i}張', str(self.Myini.Class_Name_All[i]), str('倒放插件'), 'Opposite'])
|
||
self.report.emit(10, 'Opposite', i, diameter, numberofvertices)
|
||
self.DrawResult(i, 255, 0, 0)
|
||
elif class_name.split('_')[0] == self.Myini.Class_Name_All[i].split('_')[0]:
|
||
if class_name.split('_')[0] == 'phillips':
|
||
is_NG = True
|
||
self.report.emit(0, confidences[Index.index(i)], i, diameter, numberofvertices)
|
||
self.send_output_message_Finish(
|
||
[f'第{i}張', str(self.Myini.Class_Name_All[i]), str('AI失敗'), str(class_name)])
|
||
self.DrawResult(i, 0, 0, 255)
|
||
# print(f'AI失敗 第{i}張 {self.Myini.Class_Name_All[i]} 預測結果 : {class_name}')
|
||
else:
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AOI/{self.Time_for_Record}_{class_name}_{i}_{diameter}_{numberofvertices}_{aspect_ratio}.png',
|
||
image_measure)
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AOI_Origin/{self.Time_for_Record}_{class_name}_{i}_{diameter}_{numberofvertices}_{aspect_ratio}.png',
|
||
img.Image)
|
||
self.report.emit(-1, confidences[Index.index(i)], i, diameter, numberofvertices)
|
||
self.send_output_message_Finish(
|
||
[f'第{i}張', str(self.Myini.Class_Name_All[i]), str('AOI失敗'), str(class_name)])
|
||
self.DrawResult(i, 0, 0, 255)
|
||
# print(f'AOI失敗 第{i}張 {self.Myini.Class_Name_All[i]} 預測結果 : {class_name}')
|
||
else:
|
||
is_NG = True
|
||
self.report.emit(0, confidences[Index.index(i)], i, diameter, numberofvertices)
|
||
self.send_output_message_Finish(
|
||
[f'第{i}張', str(self.Myini.Class_Name_All[i]), str('AI失敗'), str(class_name)])
|
||
self.DrawResult(i, 0, 0, 255)
|
||
# print(f'AI失敗 第{i}張 {self.Myini.Class_Name_All[i]} 預測結果 : {class_name}')
|
||
if is_NG or is_NotValid:
|
||
NG_Record = self.Myini.Class_Name_All[i].split('_')[0]
|
||
if NG_Record == 'phillips':
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AOI/{self.Time_for_Record}_{class_name}_{i}_{diameter}_{numberofvertices}.png',
|
||
image_measure)
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AOI_Origin/{self.Time_for_Record}_{class_name}_{i}_{diameter}_{numberofvertices}.png',
|
||
img.Image)
|
||
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AI/{self.Filenames_for_Record[self.Myini.Class_Name_AI.index(self.Myini.Class_Name_All[i])]}/{self.Time_for_Record}_{class_name}_{i}_{confidences[Index.index(i)]}.png',
|
||
images_ai[i].Image)
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AOI_ForAIPredict/{self.Filenames_for_Record[self.Myini.Class_Name_AI.index(self.Myini.Class_Name_All[i])]}/{self.Time_for_Record}_{class_name}_{i}_{confidences[Index.index(i)]}.png',
|
||
img.Image)
|
||
else:
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AOI/{self.Time_for_Record}_{class_name}_{i}_{diameter}_{numberofvertices}_{aspect_ratio}.png',
|
||
image_measure)
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AOI_Origin/{self.Time_for_Record}_{class_name}_{i}_{diameter}_{numberofvertices}_{aspect_ratio}.png',
|
||
img.Image)
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AI/{self.Filenames_for_Record[self.Myini.Class_Name_AI.index(NG_Record)]}/{self.Time_for_Record}_{class_name}_{i}_{confidences[Index.index(i)]}.png',
|
||
images_ai[i].Image)
|
||
cv2.imwrite(
|
||
f'./imageprocess/{self.Myini.recipe}/AOI_ForAIPredict/{self.Filenames_for_Record[self.Myini.Class_Name_AI.index(NG_Record)]}/{self.Time_for_Record}_{class_name}_{i}_{confidences[Index.index(i)]}.png',
|
||
img.Image)
|
||
print(f'!!AOI量測 : {time.time() - AOI_Time}')
|
||
|
||
def Measuresize(self, classname, image, count):
|
||
# print(classname)
|
||
# print(self.Myini.Class_Name_All[count])
|
||
if self.Myini.NotValid_dic[classname][0] == 0:
|
||
return classname, image, str(0), 0, str(0)
|
||
try:
|
||
image_measure, diameter, aspect_ratio, numberofvertices = Measure(image).measure(classname,recipe=self.Myini.recipe)#82207
|
||
except Exception as e:
|
||
diameter = 0
|
||
image_measure = image
|
||
aspect_ratio = 29
|
||
numberofvertices = 0
|
||
print(f'measure\n{e}')
|
||
self.dev_logger.error(f'measure\n{e}')
|
||
|
||
# 不一樣才進去
|
||
# AI推論為非插件錯放
|
||
# 判錯放對
|
||
if not (classname in self.Myini.Class_Name_All[count]):
|
||
# ini檔角數量不是0
|
||
# 誤判種類算出來的角數量不等於這個位置的種類算出來的角數量
|
||
# 才進去
|
||
if not (self.Myini.NotValid_NumberOfVertices_dic[classname] == 0) and not (numberofvertices == self.Myini.NotValid_NumberOfVertices_dic[classname]):
|
||
if classname == 'torx' and numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['pentalope']:
|
||
classname = 'pentalope'
|
||
elif classname == 'pentalope' and numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['torx']:
|
||
if diameter < self.Myini.MeasureSize_dic['pentalope'][0]:
|
||
pass
|
||
if diameter > self.Myini.MeasureSize_dic['pentalope'][0]:
|
||
classname = 'torx'
|
||
else:
|
||
return f'{classname}_NotValidNOV', image_measure, str(diameter), aspect_ratio, str(
|
||
numberofvertices)
|
||
elif classname == 'triwing' and numberofvertices in [5, 6]:
|
||
if diameter < self.Myini.MeasureSize_dic['pentalope'][0]:
|
||
classname = 'pentalope'
|
||
|
||
elif classname == 'slotted' and aspect_ratio > 0.4:
|
||
if diameter > self.Myini.MeasureSize_dic['square'][0] and numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['square']:
|
||
print('square in up')
|
||
classname = 'square'
|
||
|
||
elif diameter > self.Myini.MeasureSize_dic['triangle'][0] and numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['triangle']:
|
||
print('triangle in up')
|
||
classname = 'triangle'
|
||
|
||
elif self.Myini.MeasureSize_dic['triwing'][1] > diameter > self.Myini.MeasureSize_dic['triwing'][0] and numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['triwing']:
|
||
print('triwing in up')
|
||
classname = 'triwing'
|
||
|
||
elif diameter < self.Myini.MeasureSize_dic['hex'][0]:
|
||
print('hex in up')
|
||
classname = 'hex'
|
||
|
||
elif diameter > self.Myini.MeasureSize_dic['torx'][0] and numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['torx']:
|
||
print('torx in up')
|
||
classname = 'torx'
|
||
|
||
for k, d in enumerate(self.Myini.NotValid_dic[classname]):
|
||
if diameter > self.Myini.NotValid_dic[classname][-1]:
|
||
return f'{classname}_NotValid', image_measure, str(diameter), aspect_ratio, str(numberofvertices)
|
||
if diameter <= d:
|
||
if k % 2 == 1:
|
||
break
|
||
else:
|
||
return f'{classname}_NotValid', image_measure, str(diameter), aspect_ratio, str(numberofvertices)
|
||
# 判錯放錯
|
||
elif classname == 'torx':
|
||
if diameter > self.Myini.MeasureSize_dic['torx'][0]:
|
||
if numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['pentalope']:
|
||
classname = 'pentalope'
|
||
|
||
elif classname == 'pentalope':
|
||
if diameter > self.Myini.MeasureSize_dic['pentalope'][0]:
|
||
if numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['torx']:
|
||
classname = 'torx'
|
||
|
||
if classname == 'slotted':
|
||
if aspect_ratio > 0.4 and ('hex' in self.Myini.Class_Name_All[count] or 'slotted' in self.Myini.Class_Name_All[count]):
|
||
print('hex in down')
|
||
classname = 'hex'
|
||
elif numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['square'] and aspect_ratio > 0.4:
|
||
print('square in down')
|
||
classname = 'square'
|
||
elif numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['triangle'] and aspect_ratio > 0.4:
|
||
print('triangle in down')
|
||
classname = 'triangle'
|
||
elif numberofvertices == self.Myini.NotValid_NumberOfVertices_dic['torx'] and aspect_ratio > 0.4:
|
||
print('torx in down')
|
||
classname = 'torx'
|
||
|
||
if classname == 'opposite':
|
||
return f'Bottom', image_measure, str(diameter), aspect_ratio, str(numberofvertices)
|
||
|
||
if classname in ['torxtamperproof'] and self.Myini.recipe == 'Milwaukee':
|
||
size = 0
|
||
for i in self.Myini.MeasureSize_Approx_ArcLength_dic[classname]:
|
||
if (aspect_ratio - i) > 0:
|
||
size += 1
|
||
|
||
if not (self.Myini.Class_Name_All[count] == f'{classname}_{size + 1}'):
|
||
size_diameter = 0
|
||
for i in self.Myini.MeasureSize_dic[classname]:
|
||
if (diameter - i) > 0:
|
||
size_diameter += 1
|
||
size_Approx_Area = 0
|
||
for i in self.Myini.MeasureSize_Approx_Area_dic[classname]:
|
||
if (numberofvertices - i) > 0:
|
||
size_Approx_Area += 1
|
||
|
||
if self.Myini.Class_Name_All[count] in [f'{classname}_{size_Approx_Area + 1}',
|
||
f'{classname}_{size_diameter + 1}']:
|
||
return self.Myini.Class_Name_All[count], image_measure, str(diameter), aspect_ratio, str(
|
||
numberofvertices)
|
||
else:
|
||
return f'{classname}_{size + 1}', image_measure, str(diameter), aspect_ratio, str(
|
||
numberofvertices)
|
||
|
||
else:
|
||
return f'{classname}_{size + 1}', image_measure, str(diameter), aspect_ratio, str(numberofvertices)
|
||
else:
|
||
if self.Myini.MeasureSize_dic[classname][0] == 0:
|
||
return classname, image_measure, str(diameter), aspect_ratio, str(numberofvertices)
|
||
size = 0
|
||
for i in self.Myini.MeasureSize_dic[classname]:
|
||
if (diameter - i) > 0:
|
||
size += 1
|
||
return f'{classname}_{size + 1}', image_measure, str(diameter), aspect_ratio, str(numberofvertices)
|
||
|
||
def ROI(self, Image):
|
||
try:
|
||
result = [Image_and_Index(Image[roi_data.Y_start:roi_data.Y_stop, roi_data.X_start:roi_data.X_stop], i) for i, roi_data in enumerate(self.Myini.ROI_List)]
|
||
# result = list()
|
||
# for i, roi_data in enumerate(self.Myini.ROI_List):
|
||
|
||
# result.append(Image_and_Index(Image[roi_data.Y_start:roi_data.Y_stop, roi_data.X_start:roi_data.X_stop], i))
|
||
except Exception as e:
|
||
self.dev_logger.error(f'ROI\n{e}')
|
||
return result
|
||
|
||
|
||
def FindMaxContours(self, contours):
|
||
area = []
|
||
for contour in contours:
|
||
area.append(cv2.contourArea(contour))
|
||
max_idx = np.argmax(np.array(area))
|
||
return max_idx
|
||
|
||
def Test_Measure(self):
|
||
start = time.time()
|
||
file_list = os.listdir(self.Input_FilePath)
|
||
|
||
for k, file in enumerate(file_list):
|
||
print(f' ********************************************** file_list = {file[3:].lower()} **********************************************')
|
||
img_filenames = os.listdir(f'{self.Input_FilePath}/{file}')
|
||
diameter_array = list()
|
||
aspect_ratio_array = list()
|
||
area_array = list()
|
||
classname_size_array = list()
|
||
for img_filename in img_filenames:
|
||
def size_judge(d, a_approx, a, img_filename):
|
||
def Get_Locate(f):
|
||
nums = [number for number in re.findall(r'\d+', f) if int(number) >= 0]
|
||
if len(nums[-2]) == 6:
|
||
num = int(nums[-1])
|
||
else:
|
||
num = int(nums[-4])
|
||
return num
|
||
if self.Myini.MeasureSize_dic[file[3:].lower()][0] == 0:
|
||
cs = file[3:].lower()
|
||
elif self.Myini.MeasureSize_Approx_ArcLength_dic[file[3:].lower()] == 0:
|
||
size = 0
|
||
for i in self.Myini.MeasureSize_dic[file[3:].lower()]:
|
||
if (d - i) > 0:
|
||
size += 1
|
||
cs = f'{file[3:].lower()}_{size + 1}'
|
||
else:
|
||
locate = Get_Locate(img_filename)
|
||
size = 0
|
||
for i in self.Myini.MeasureSize_Approx_Area_dic[file[3:].lower()]:
|
||
if (a_approx - i) > 0:
|
||
size += 1
|
||
|
||
if a_approx < (self.Myini.MeasureSize_Approx_Area_dic[file[3:].lower()][size-1] if size > 0 else 0) * 1.02 or a_approx > (self.Myini.MeasureSize_Approx_Area_dic[file[3:].lower()][size] if size < len(self.Myini.MeasureSize_Approx_Area_dic[file[3:].lower()]) else 100000) * 0.98 or not (size + 1 + locate == 22):
|
||
size_diameter = 0
|
||
for i in self.Myini.MeasureSize_dic[file[3:].lower()]:
|
||
if (d - i) > 0:
|
||
size_diameter += 1
|
||
|
||
size_Area = 0
|
||
for i in self.Myini.MeasureSize_Approx_ArcLength_dic[file[3:].lower()]:
|
||
if (a - i) > 0:
|
||
size_Area += 1
|
||
cs = f'{file[3:].lower()}_{size + 1}_{size_diameter + 1}_{size_Area + 1}'
|
||
else:
|
||
cs = f'{file[3:].lower()}_{size + 1}'
|
||
return cs
|
||
|
||
print(img_filename)
|
||
# 讀取
|
||
path = f'{self.Input_FilePath}/{file}/{img_filename}'
|
||
InputImage = cv2.imread(f'{self.Input_FilePath}/{file}/{img_filename}')
|
||
measure_object = Measure(InputImage)
|
||
# image, diameter, aspect_ratio, vertices = measure_object.measure_for_KleinTool(file[3:].lower())
|
||
image, diameter, aspect_ratio, vertices = measure_object.measure(file[3:].lower())
|
||
|
||
classname_size = size_judge(diameter, aspect_ratio, vertices, img_filename)
|
||
|
||
self.Measure_dic_Image[img_filename] = [image, measure_object.Histogram, measure_object.Remedy]
|
||
os.makedirs(f'./Measure/{file}', exist_ok=True)
|
||
cv2.imwrite(f'./Measure/{file}/{img_filename}', image)
|
||
diameter_array.append(diameter)
|
||
aspect_ratio_array.append(aspect_ratio)
|
||
area_array.append(vertices)
|
||
classname_size_array.append(classname_size)
|
||
def myFunc(e):
|
||
return e[2]
|
||
result = list(zip(img_filenames, classname_size_array, diameter_array, aspect_ratio_array, area_array))
|
||
result.sort(reverse=False, key=myFunc)
|
||
self.Measure_dic[file[3:].lower()] = result
|
||
self.write_csv(file, result)
|
||
self.Measure_Step.emit(file[3:].lower())
|
||
|
||
file_list = os.listdir('./Measure_CSV/csv')
|
||
# 初始化一個空的DataFrame
|
||
merged_df = pd.DataFrame()
|
||
# 迴圈讀取每個CSV檔案,並將其合併到merged_df中
|
||
for file in file_list:
|
||
df = pd.read_csv(fr'./Measure_CSV/csv/{file}')
|
||
merged_df = pd.concat([merged_df, df], axis=1)
|
||
|
||
# 將合併後的DataFrame寫入新的CSV檔案
|
||
merged_df.to_csv(r'./Measure_CSV/Combined_Result.csv', index=False)
|
||
|
||
|
||
end = time.time()
|
||
print(f' ------------------------------------ 耗時 --> {(end - start)} ------------------------------------')
|
||
def write_csv(self, file_path, data):
|
||
with open(rf'.\Measure_CSV\csv\{file_path}.csv', 'w', newline='') as file:
|
||
writer = csv.writer(file)
|
||
writer.writerow([f'{file_path}', 'classname_size', 'diameters', 'aspect ratio', 'Approximate contour angle number']) # 寫入標題行
|
||
# 寫入每個 epoch 的 mean_loss 和 acc
|
||
for filename, classname_size, diameters, a, area in data:
|
||
writer.writerow([filename, diameters, a, area])
|
||
|
||
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 send_output_message_Finish(self, message):
|
||
# 事件物件,寫了新文章
|
||
self.output_finish.emit(message[0], message[1], message[2], message[3])
|
||
|
||
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 Predict_Images_YOLOv8(self, images, models_Index):
|
||
try:
|
||
results = self.models[models_Index]([img.Image for img in images])
|
||
result_Max_Index = [result.probs.top1 for result in results]
|
||
class_numbers = [self.Myini.Class_Name_AI[int(Index)] for Index in result_Max_Index]
|
||
|
||
confidences = ['29' for i, Index in enumerate(result_Max_Index)]
|
||
except Exception as e:
|
||
self.dev_logger.error(f'Predict_Images\n{e}')
|
||
return class_numbers, confidences, [index.Image_Index for index in images]
|
||
|
||
|
||
def DrawImage_VarificationCode(image, varification_code):
|
||
Image_resize_ToSee = cv2.resize(image, (500, 500), interpolation=cv2.INTER_CUBIC)
|
||
cv2.rectangle(Image_resize_ToSee, (100, 100), (130, 50), (255), -1)
|
||
text_varification_code = '{0}'.format(varification_code)
|
||
cv2.putText(Image_resize_ToSee, text_varification_code, (100, 100), cv2.FONT_HERSHEY_PLAIN, 5, (0), 1, cv2.LINE_AA)
|
||
return Image_resize_ToSee
|
||
|
||
class Measure:
|
||
def __init__(self, inputimage, dev_logger=None):
|
||
# if dev_logger == None:
|
||
# def iniLogging():
|
||
# 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\Measure\{logging_FileName}_dev.log', mode='a')
|
||
# file_handler.setFormatter(formatter)
|
||
# self.dev_logger.addHandler(file_handler)
|
||
# iniLogging()
|
||
# else:
|
||
# self.dev_logger = dev_logger
|
||
|
||
self.InputImage = copy.deepcopy(inputimage)
|
||
self.Recipe_NoMask = ['KleinTool']
|
||
self.Histogram = []
|
||
self.Remedy = []
|
||
def measure(self, classnumber, recipe='Milwaukee'):
|
||
print(f'measure_{classnumber}')
|
||
if recipe == 'Milwaukee':
|
||
aspect_ratios = 29
|
||
if len(self.InputImage.shape) == 3:
|
||
inputimage = cv2.cvtColor(self.InputImage, cv2.COLOR_BGR2GRAY) # 圖像存儲使用8-8-8 24位RGB格式
|
||
else:
|
||
inputimage = copy.deepcopy(self.InputImage)
|
||
self.InputImage = cv2.cvtColor(self.InputImage, cv2.COLOR_GRAY2BGR)
|
||
# GaussianBlur = cv2.GaussianBlur(inputimage, (31, 31), 0)
|
||
|
||
maskimage = self.GetMask(inputimage)
|
||
if maskimage is []:
|
||
return self.InputImage, 0, 0, 0
|
||
|
||
if classnumber == 'spanner' or classnumber == '03_Spanner':
|
||
_, Threshold_Image = cv2.threshold(inputimage, 60, 255, cv2.THRESH_BINARY)
|
||
count_zero_pixels = (Threshold_Image == 255).sum()
|
||
# print(count_zero_pixels)
|
||
return Threshold_Image, count_zero_pixels, 0, 0
|
||
elif classnumber == 'square':
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 130, 255, cv2.THRESH_BINARY)
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
elif classnumber == 'hextamperproof':
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 120, 255, cv2.THRESH_BINARY)
|
||
cv2.imwrite(f'./Measure/ht_1_Threshold_Image.png', Threshold_Image)
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
cv2.imwrite(f'./Measure/ht_2_maskimage.png', maskimage)
|
||
cv2.imwrite(f'./Measure/ht_3_Threshold_Image_And.png', Threshold_Image)
|
||
elif classnumber == 'triwing':
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 100, 255, cv2.THRESH_BINARY) # 120
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
elif classnumber == 'torxtamperproof':
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 120, 255, cv2.THRESH_BINARY) # 120
|
||
cv2.imwrite(f'./Measure/tt_1_Threshold_Image.png', Threshold_Image)
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
cv2.imwrite(f'./Measure/tt_2_maskimage.png', maskimage)
|
||
cv2.imwrite(f'./Measure/tt_3_Threshold_Image_And.png', Threshold_Image)
|
||
elif classnumber == 'slotted':
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 120, 255, cv2.THRESH_BINARY) # 150
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
else:
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 136, 255, cv2.THRESH_BINARY)#145
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
|
||
contours, _ = cv2.findContours(Threshold_Image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
||
if classnumber == 'torxtamperproof':
|
||
max_idx = self.FindMaxContours_For_torxtamperproof(contours)
|
||
else:
|
||
max_idx = self.FindMaxContours_For_Other(contours)
|
||
if max_idx > -1:
|
||
torxtamperproof_area = 0
|
||
torxtamperproof_approx_arcLength = 0
|
||
torxtamperproof_approx_area = 0
|
||
square_area = 0
|
||
vertices = 0
|
||
(x, y), radius = cv2.minEnclosingCircle(contours[max_idx])
|
||
center = (int(x), int(y))
|
||
|
||
radius_int = int(radius)
|
||
|
||
if classnumber in ['hextamperproof']:
|
||
count = 1
|
||
mask = np.zeros(Threshold_Image.shape, dtype='uint8')
|
||
cv2.circle(mask, center, radius_int, (255, 255, 255), -1)
|
||
|
||
new_image = cv2.bitwise_and(inputimage, mask)
|
||
cv2.imwrite(f'./Measure/ht_4_new_image.png', new_image)
|
||
mean_value = cv2.mean(new_image[center[1] - radius_int:center[1] + radius_int,center[0] - radius_int:center[0] + radius_int]) # 計算套頭形狀部分的亮度平均值
|
||
threshold_value = int(mean_value[0] * 1.2) # 計算inputimage的閾值
|
||
|
||
ret, Threshold_Image_mean_value = cv2.threshold(inputimage, threshold_value, 255,cv2.THRESH_BINARY) # 二值化
|
||
self.Histogram = cv2.bitwise_not(Threshold_Image_mean_value)
|
||
contours_hextamperproof, hierarchy = cv2.findContours(cv2.bitwise_not(Threshold_Image_mean_value),
|
||
cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
|
||
|
||
i = 0
|
||
hierach_id = 0
|
||
inner_class = []
|
||
while not (hierarchy[0][i][2] == -1) and hierarchy[0][i][1] == -1 and i < len(
|
||
contours_hextamperproof) and not (hierarchy[0][i][3] == -1):
|
||
hierach_id = i
|
||
i += 1
|
||
# print(hierach_id, hierarchy[0][hierach_id])
|
||
while not (hierarchy[0][hierach_id][0] == -1):
|
||
inner_class.append(contours_hextamperproof[hierach_id])
|
||
hierach_id = hierarchy[0][hierach_id][0]
|
||
# print(hierach_id, hierarchy[0][hierach_id])
|
||
|
||
|
||
Max_contour_index_hextamperproof = self.FindMaxContours_For_hextamperproof(inner_class) # 找最大輪廓
|
||
|
||
(x, y), radius = cv2.minEnclosingCircle(inner_class[Max_contour_index_hextamperproof]) # 找最大輪廓最小外接園的直徑和圓心
|
||
center = (int(x), int(y))
|
||
radius_int = int(radius)
|
||
cv2.circle(self.InputImage, center, radius_int, (0, 242, 255), 1)
|
||
|
||
elif classnumber in ['torxtamperproof']:
|
||
mask = np.zeros(Threshold_Image.shape, dtype='uint8')
|
||
# self.Histogram = cv2.bitwise_and(inputimage, maskimage, inputimage)
|
||
cv2.circle(mask, center, radius_int, (255, 255, 255), -1)
|
||
new_image = cv2.bitwise_and(inputimage, mask)
|
||
cv2.imwrite(f'./Measure/tt_4_new_image.png', new_image)
|
||
mean_value = cv2.mean(new_image[center[1]-radius_int:center[1]+radius_int, center[0]-radius_int:center[0]+radius_int])
|
||
threshold_value = int(mean_value[0]*1.36)#1.36
|
||
ret, Threshold_Image_mean_value = cv2.threshold(inputimage, threshold_value, 255, cv2.THRESH_BINARY)
|
||
contours_torxtamperproof, _ = cv2.findContours(cv2.bitwise_and(Threshold_Image_mean_value, maskimage), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
||
|
||
testContoursImage = self.InputImage.copy()
|
||
cv2.drawContours(testContoursImage, contours_torxtamperproof, -1, (255, 0, 0), 1)
|
||
cv2.imwrite(f'./Measure/tt_5_testContoursImage.png', testContoursImage)
|
||
|
||
Max_contour_index_torxtamperproof = self.FindMaxContours_For_torxtamperproof(contours_torxtamperproof)
|
||
cv2.drawContours(self.InputImage, contours_torxtamperproof, Max_contour_index_torxtamperproof, (99, 242, 50), 1)
|
||
|
||
testContoursImage2 = self.InputImage.copy()
|
||
cv2.drawContours(testContoursImage2, contours_torxtamperproof, Max_contour_index_torxtamperproof, (255, 0, 0), 1)
|
||
cv2.imwrite(f'./Measure/tt_6_testContoursImage.png', testContoursImage2)
|
||
|
||
(x, y), radius = cv2.minEnclosingCircle(contours_torxtamperproof[Max_contour_index_torxtamperproof])
|
||
|
||
epsilon = 0.045 * cv2.arcLength(contours_torxtamperproof[Max_contour_index_torxtamperproof], True)
|
||
approx = cv2.approxPolyDP(contours_torxtamperproof[Max_contour_index_torxtamperproof], epsilon, True)
|
||
torxtamperproof_approx_area = cv2.contourArea(approx)
|
||
torxtamperproof_approx_arcLength = cv2.arcLength(approx, True)
|
||
if torxtamperproof_approx_arcLength > 80 and torxtamperproof_approx_area < 450.5:
|
||
torxtamperproof_approx_arcLength = 80.292929
|
||
|
||
elif classnumber in ['square', 'slotted', 'triangle']:
|
||
mask = np.zeros(Threshold_Image.shape, dtype='uint8')
|
||
# self.Histogram = cv2.bitwise_and(inputimage, maskimage, inputimage)
|
||
cv2.circle(mask, center, radius_int, (255, 255, 255), -1)
|
||
new_image = cv2.bitwise_and(inputimage, mask)
|
||
# self.Histogram = new_image[center[1]-radius_int:center[1]+radius_int, center[0]-radius_int:center[0]+radius_int]
|
||
self.Histogram = mask
|
||
mean_value = cv2.mean(new_image[center[1] - radius_int:center[1] + radius_int,
|
||
center[0] - radius_int:center[0] + radius_int])
|
||
threshold_value = int(mean_value[0]*1.1)
|
||
# print(f'mean_value = {mean_value[0]} mean_value\' = {threshold_value}')
|
||
ret, Threshold_Image_mean_value = cv2.threshold(inputimage, threshold_value, 255, cv2.THRESH_BINARY)
|
||
# self.Histogram = Threshold_Image_mean_value
|
||
contours_square,_ = cv2.findContours(cv2.bitwise_and(Threshold_Image_mean_value, maskimage), cv2.RETR_EXTERNAL,
|
||
cv2.CHAIN_APPROX_NONE)
|
||
Max_contour_index_square = self.FindMaxContours_For_torxtamperproof(contours_square)
|
||
square_area = cv2.contourArea(contours_square[Max_contour_index_square])
|
||
cv2.drawContours(self.InputImage, contours_square, Max_contour_index_square, (99, 242, 50), 1)
|
||
# 近似轮廓
|
||
epsilon = 0.05 * cv2.arcLength(contours_square[Max_contour_index_square], True)
|
||
approx = cv2.approxPolyDP(contours_square[Max_contour_index_square], epsilon, True)
|
||
cv2.drawContours(self.InputImage, [approx], -1, (0, 0, 255), 1)
|
||
vertices = len(approx)
|
||
# print(f'vertices up = {classnumber} {vertices}')
|
||
else:
|
||
self.Histogram = maskimage
|
||
cv2.circle(self.InputImage, center, radius_int, (255, 0, 0), 1)
|
||
|
||
# 获取轮廓的顶点数
|
||
# print((radius*2)*22*0.001)
|
||
# print(min(rect[1][:])*22*0.001)
|
||
# print(max(rect[1][:])*22*0.001)
|
||
# GaussianBlur = cv2.cvtColor(GaussianBlur, cv2.COLOR_GRAY2BGR) # 圖像存儲使用8-8-8 24位RGB格式
|
||
radius = round(radius * 2 * 22 * 0.001, 3)#54.55
|
||
# cv2.imwrite(f'./Measure/{classnumber}_{radius}.png', Threshold_Image)
|
||
if classnumber in ['torx', 'pentalope', 'torxtamperproof']:
|
||
# 近似轮廓
|
||
if classnumber in ['pentalope'] and radius < 0.377:
|
||
epsilon = 0.045 * cv2.arcLength(contours[max_idx], True)
|
||
else:
|
||
epsilon = 0.04 * cv2.arcLength(contours[max_idx], True)
|
||
approx = cv2.approxPolyDP(contours[max_idx], epsilon, True)
|
||
cv2.drawContours(self.InputImage, [approx], -1, (0, 0, 255), 1)
|
||
vertices = len(approx)
|
||
elif classnumber in ['triangle', 'phillips', 'slotted']:
|
||
# 近似轮廓
|
||
epsilon = 0.05 * cv2.arcLength(contours[max_idx], True)
|
||
approx = cv2.approxPolyDP(contours[max_idx], epsilon, True)
|
||
cv2.drawContours(self.InputImage, [approx], -1, (0, 0, 255), 1)
|
||
vertices = len(approx)
|
||
# print(f'vertices under = {classnumber} {vertices}')
|
||
elif classnumber in ['triwing']:
|
||
epsilon = 0.07 * cv2.arcLength(contours[max_idx], True)
|
||
approx = cv2.approxPolyDP(contours[max_idx], epsilon, True)
|
||
cv2.drawContours(self.InputImage, [approx], -1, (0, 0, 255), 1)
|
||
vertices = len(approx)
|
||
|
||
aspect_ratios = self.draw_min_rect(self.InputImage, contours, max_idx)
|
||
if classnumber in ['torxtamperproof']:
|
||
return self.InputImage, radius, torxtamperproof_approx_arcLength, torxtamperproof_approx_area
|
||
# elif classnumber in ['square', 'slotted', 'triangle']:
|
||
# return self.InputImage, radius, square_area, vertices
|
||
else:
|
||
# if classnumber in ['square', 'slotted', 'triangle']:
|
||
# print(f'aspect_ratios = {classnumber} {aspect_ratios}')
|
||
return self.InputImage, radius, aspect_ratios, vertices
|
||
else:
|
||
return self.InputImage, 0, 0, 0
|
||
else:
|
||
return self.measure_for_KleinTool(classnumber)
|
||
# return self.AI_measure_for_KleinTool(classnumber)
|
||
def measure_for_KleinTool(self, classnumber):
|
||
if len(self.InputImage.shape) == 3:
|
||
inputimage = cv2.cvtColor(self.InputImage, cv2.COLOR_BGR2GRAY) # 圖像存儲使用8-8-8 24位RGB格式
|
||
else:
|
||
inputimage = copy.deepcopy(self.InputImage)
|
||
self.InputImage = cv2.cvtColor(self.InputImage, cv2.COLOR_GRAY2BGR)
|
||
# GaussianBlur = cv2.GaussianBlur(inputimage, (31, 31), 0)
|
||
|
||
self.Remedy = inputimage
|
||
self.Histogram = inputimage
|
||
if classnumber == 'spanner' or classnumber == '03_Spanner':
|
||
inputimage = self.Unsharpen(inputimage)
|
||
maskimage = self.GetMask_for_KleinTool(inputimage, 55, 60)
|
||
maskimage_spanner = self.GetMask_for_KleinTool(inputimage, 80, 60)
|
||
|
||
self.Histogram = maskimage
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 205, 255, cv2.THRESH_BINARY)
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
self.Remedy = Threshold_Image
|
||
elif classnumber == 'square':
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 130, 255, cv2.THRESH_BINARY)
|
||
elif classnumber == 'hextamperproof':
|
||
inputimage = self.Unsharpen(inputimage)
|
||
self.Remedy = inputimage
|
||
maskimage = self.GetMask_for_KleinTool(inputimage)
|
||
self.Histogram = maskimage
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 140, 255, cv2.THRESH_BINARY)
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
elif classnumber == 'triwing':
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 170, 255, cv2.THRESH_BINARY) # 150
|
||
elif classnumber == 'torxtamperproof':
|
||
inputimage = self.Unsharpen(inputimage)
|
||
self.Remedy = inputimage
|
||
maskimage = self.GetMask_for_KleinTool(inputimage, 55)
|
||
self.Histogram = maskimage
|
||
Get_MeanValue = cv2.mean(cv2.bitwise_and(inputimage, maskimage))[0] # 計算套頭形狀部分的亮度平均值
|
||
# print(f'第一次平均值 = {Get_MeanValue}')
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 115, 255, cv2.THRESH_BINARY) # 150
|
||
cv2.bitwise_and(Threshold_Image, maskimage, Threshold_Image)
|
||
cv2.imwrite(f'./Measure/1_Threshold_Image.png', Threshold_Image)
|
||
else:
|
||
ret, Threshold_Image = cv2.threshold(inputimage, 180, 255, cv2.THRESH_BINARY)#150
|
||
contours, _ = cv2.findContours(Threshold_Image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
||
|
||
if classnumber in ['hextamperproof']:
|
||
max_idx = self.FindMaxContours(contours)
|
||
cv2.drawContours(self.InputImage, contours, -1, (99, 242, 50), 1)
|
||
elif classnumber in ['torxtamperproof']:
|
||
max_idx = self.FindMaxContours_For_torxtamperproof(
|
||
contours, 150) # 找最大輪廓
|
||
else:
|
||
max_idx = self.FindMaxContours_For_Other(contours, 150)
|
||
|
||
if max_idx > -1:
|
||
vertices = 0
|
||
(x, y), radius = cv2.minEnclosingCircle(contours[max_idx])
|
||
center = (int(x), int(y))
|
||
radius_int = int(radius)
|
||
if classnumber in ['torxtamperproof']:
|
||
mask = np.zeros(Threshold_Image.shape, dtype='uint8')
|
||
cv2.circle(mask, center, radius_int, (255, 255, 255), -1)
|
||
cv2.circle(self.InputImage, center, radius_int, (0, 0, 255), 2)
|
||
new_image = cv2.bitwise_and(inputimage, mask)
|
||
# cv2.imwrite(f'./Measure/new_image.png', new_image)
|
||
mean_value = cv2.mean(new_image[center[1] - radius_int:center[1] + radius_int,center[0] - radius_int:center[0] + radius_int]) # 計算套頭形狀部分的亮度平均值
|
||
threshold_value = int(mean_value[0] * 1.15) # 計算inputimage的閾值
|
||
# print(f'threshold_value = {threshold_value}')
|
||
self.Histogram = cv2.bitwise_and(new_image, maskimage)
|
||
ret, Threshold_Image_mean_value = cv2.threshold(cv2.bitwise_and(new_image, maskimage),threshold_value, 255, cv2.THRESH_BINARY) # 二值化
|
||
# self.Histogram = new_image
|
||
contours_torxtamperproof, _ = cv2.findContours(Threshold_Image_mean_value, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)# 找輪廓
|
||
Max_contour_index_torxtamperproof = self.FindMaxContours_For_torxtamperproof(contours_torxtamperproof) # 找最大輪廓
|
||
# cv2.drawContours(self.InputImage, inner_class, -1, (99, 242, 50), 1)
|
||
(x, y), radius = cv2.minEnclosingCircle(contours_torxtamperproof[Max_contour_index_torxtamperproof]) # 找最大輪廓最小外接園的直徑和圓心
|
||
center = (int(x), int(y))
|
||
radius_int = int(radius)
|
||
cv2.circle(self.InputImage, center, radius_int, (0, 242, 255), 1)
|
||
elif classnumber in ['hextamperproof']:
|
||
mask = np.zeros(Threshold_Image.shape, dtype='uint8')
|
||
cv2.circle(mask, center, radius_int - 3, (255, 255, 255), -1)
|
||
cv2.circle(self.InputImage, center, radius_int - 3, (0, 0, 255), 2)
|
||
|
||
new_image = cv2.bitwise_and(inputimage, mask)
|
||
new_image = cv2.add(new_image, cv2.bitwise_not(mask))
|
||
cv2.imwrite(f'./Measure/new_image.png', new_image)
|
||
x_start = center[0] - radius_int if center[0] - radius_int >= 0 else 0
|
||
y_start = center[1] - radius_int if center[1] - radius_int >= 0 else 0
|
||
x_end = center[0] + radius_int if center[0] + radius_int < Threshold_Image.shape[0] else Threshold_Image.shape[0]-1
|
||
y_end = center[1] + radius_int if center[1] + radius_int < Threshold_Image.shape[0] else Threshold_Image.shape[0]-1
|
||
|
||
mean_value = cv2.mean(inputimage[y_start:y_end, x_start:x_end]) # 計算套頭形狀部分的亮度平均值
|
||
# print(f'mean_value = {mean_value}')
|
||
threshold_value = int(mean_value[0] * 0.9) # 計算inputimage的閾值
|
||
# print(f'threshold_value = {threshold_value}')
|
||
ret, Threshold_Image_mean_value = cv2.threshold(new_image, threshold_value, 255,cv2.THRESH_BINARY) # 二值化
|
||
self.Histogram = Threshold_Image_mean_value
|
||
contours_hextamperproof, _ = cv2.findContours(cv2.bitwise_not(Threshold_Image_mean_value), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)# 找輪廓
|
||
# i = 0
|
||
# hierach_id = 0
|
||
# inner_class = []
|
||
# try:
|
||
# while not (contours_hextamperproof[2][0][i][2] == -1) and contours_hextamperproof[2][0][i][1] == -1 and i < len(contours_hextamperproof[1]) and not (contours_hextamperproof[2][0][i][3] == -1):
|
||
# hierach_id = i
|
||
# i += 1
|
||
# except:
|
||
# cv2.imwrite(f'./Measure/error.png', Threshold_Image_mean_value)
|
||
# while not (contours_hextamperproof[2][0][hierach_id][0] == -1):
|
||
# inner_class.append(contours_hextamperproof[1][hierach_id])
|
||
# hierach_id = contours_hextamperproof[2][0][hierach_id][0]
|
||
|
||
|
||
Max_contour_index_hextamperproof = self.FindMaxContours_For_hextamperproof(contours_hextamperproof, 10) # 找最大輪廓
|
||
# print(f'Max_contour_index_hextamperproof = {Max_contour_index_hextamperproof}')
|
||
# cv2.drawContours(self.InputImage, contours_hextamperproof, -1, (99, 242, 50), 1)
|
||
try:
|
||
(x, y), radius = cv2.minEnclosingCircle(
|
||
contours_hextamperproof[Max_contour_index_hextamperproof]) # 找最大輪廓最小外接園的直徑和圓心
|
||
# print(f'area = {cv2.contourArea(contours_hextamperproof[Max_contour_index_hextamperproof])}')
|
||
except:
|
||
cv2.imwrite(f'./Measure/error_minEnclosingCircle.png', Threshold_Image_mean_value)
|
||
|
||
center = (int(x), int(y))
|
||
radius_int = int(radius)
|
||
cv2.circle(self.InputImage, center, radius_int, (0, 242, 255), 1)
|
||
elif classnumber == 'spanner':
|
||
new_image = cv2.bitwise_and(inputimage, maskimage)
|
||
ret, Threshold_Image_Overcircle = cv2.threshold(cv2.bitwise_and(inputimage, maskimage_spanner), 200,
|
||
255, cv2.THRESH_BINARY) # 二值化
|
||
ret, Threshold_Image_extend = cv2.threshold(new_image, 230, 255, cv2.THRESH_BINARY) # 二值化
|
||
kernal = np.ones((9, 9), np.uint8)
|
||
|
||
Threshold_Image_extend = cv2.morphologyEx(Threshold_Image_extend, cv2.MORPH_DILATE, kernel=kernal,
|
||
iterations=3)
|
||
|
||
new_image = cv2.bitwise_or(new_image,
|
||
cv2.bitwise_and(Threshold_Image_extend, Threshold_Image_Overcircle))
|
||
|
||
mean_value = cv2.mean(new_image[center[1] - radius_int:center[1] + radius_int,
|
||
center[0] - radius_int:center[0] + radius_int]) # 計算套頭形狀部分的亮度平均值
|
||
threshold_value = int(mean_value[0] * 0.9) # 計算inputimage的閾值
|
||
# print(f'threshold_value = {threshold_value}')
|
||
self.Histogram = new_image
|
||
self.Remedy = Threshold_Image_Overcircle
|
||
self.extend = Threshold_Image_extend
|
||
|
||
ret, Threshold_Image_mean_value = cv2.threshold(new_image, threshold_value,
|
||
255, cv2.THRESH_BINARY) # 二值化
|
||
cv2.imwrite(f'./Measure/Threshold_Image_mean_value.png', Threshold_Image_mean_value)
|
||
kernal = np.ones((3, 3), np.uint8)
|
||
|
||
Threshold_Image_mean_value = cv2.morphologyEx(Threshold_Image_mean_value, cv2.MORPH_OPEN, kernel=kernal,
|
||
iterations=1)
|
||
Threshold_Image_mean_value = cv2.morphologyEx(Threshold_Image_mean_value, cv2.MORPH_CLOSE,
|
||
kernel=kernal, iterations=3)
|
||
|
||
# correct_value = correct(Threshold_Image)
|
||
# rotate_image = rotate(Threshold_Image, correct_value, center)
|
||
x_projection = abs(
|
||
np.sort([k for k, x in enumerate(np.sum(Threshold_Image_mean_value, axis=0)) if x > 0])[-1] -
|
||
np.sort([k for k, x in enumerate(np.sum(Threshold_Image_mean_value, axis=0)) if x > 0])[0])
|
||
y_projection = abs(
|
||
np.sort([k for k, x in enumerate(np.sum(Threshold_Image_mean_value, axis=1)) if x > 0])[-1] -
|
||
np.sort([k for k, x in enumerate(np.sum(Threshold_Image_mean_value, axis=1)) if x > 0])[0])
|
||
|
||
# count_zero_pixels = (Threshold_Image == 255).sum()
|
||
# print(count_zero_pixels)
|
||
return Threshold_Image_mean_value, math.sqrt(
|
||
(x_projection * x_projection + y_projection * y_projection)), x_projection, y_projection
|
||
|
||
else:
|
||
self.Histogram = Threshold_Image
|
||
cv2.circle(self.InputImage, center, radius_int, (255, 0, 0), 1)
|
||
radius = round(radius * 2 * 22 * 0.001, 3) # 54.55
|
||
aspect_ratios = self.draw_min_rect(self.InputImage, contours, max_idx)
|
||
return self.InputImage, radius, aspect_ratios, vertices
|
||
# radius = round(radius * 2 * 22 * 0.001, 3) # 54.55
|
||
return self.InputImage, radius, 29, vertices
|
||
|
||
else:
|
||
self.Histogram = maskimage
|
||
return self.InputImage, 0, 0, 0
|
||
def GetMask(self, image):
|
||
ret, maskimage = cv2.threshold(image, 90, 255, cv2.THRESH_BINARY_INV)#75
|
||
self.Histogram = maskimage
|
||
contours, _ = cv2.findContours(maskimage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
||
|
||
max_idx = self.FindMaxContours(contours)
|
||
|
||
if max_idx > -1:
|
||
(x, y, w, h) = cv2.boundingRect(contours[max_idx])
|
||
mask = np.zeros(maskimage.shape, dtype='uint8')
|
||
cv2.rectangle(mask, (x, y), (x + w, y + h), (255, 255, 255), -1)
|
||
cv2.imwrite(f'./Measure/maskimage.png', mask)
|
||
return mask
|
||
else:
|
||
print('errer --> GetMask')
|
||
return []
|
||
def GetMask_for_KleinTool(self, image, max_size=65, threshold_value=75):
|
||
ret, maskimage = cv2.threshold(image, threshold_value, 255, cv2.THRESH_BINARY_INV)
|
||
contours, _ = cv2.findContours(maskimage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
||
max_idx = self.FindMaxContours(contours)
|
||
if max_idx > -1:
|
||
mask = np.zeros(maskimage.shape, dtype='uint8')
|
||
(x, y), radius = cv2.minEnclosingCircle(contours[max_idx])
|
||
center = (int(x), int(y))
|
||
radius_int = int(radius)
|
||
# print(f'GetMask_for_KleinTool --> radius_int = {radius_int}')
|
||
cv2.circle(mask, center, max_size, (255, 255, 255), -1)
|
||
# cv2.drawContours(mask, contours, max_idx, (255, 255, 255), -1)
|
||
cv2.imwrite(f'./Measure/maskimage.png', mask)
|
||
return mask
|
||
else:
|
||
# self.dev_logger.error(f'GetMask_for_KleinTool max_idx == -1')
|
||
print('error --> GetMask')
|
||
return []
|
||
def Get_area(self, Threshold_img, x, y, radius):
|
||
mask = np.zeros(Threshold_img.shape, dtype='uint8')
|
||
center = (int(x), int(y))
|
||
radius_int = int(radius)
|
||
cv2.circle(mask, center, radius_int, (255, 255, 255), -1)
|
||
new_image = cv2.bitwise_and(cv2.bitwise_not(Threshold_img), mask)
|
||
cv2.imwrite(f'./Measure/new_image.png', new_image)
|
||
def Unsharpen(self, inputimage):
|
||
# USM锐化增强方法(Unsharpen Mask)
|
||
# 先对原图高斯模糊,用原图减去系数x高斯模糊的图像
|
||
# 再把值Scale到0~255的RGB像素范围
|
||
# 优点:可以去除一些细小细节的干扰和噪声,比卷积更真实
|
||
# (原图像-w*高斯模糊)/(1-w);w表示权重(0.1~0.9),默认0.6
|
||
|
||
# sigma = 5、15、25
|
||
blur_img = cv2.GaussianBlur(inputimage, (0, 0), 5)
|
||
usm = cv2.addWeighted(inputimage, 1.5, blur_img, -0.5, 0)
|
||
# cv.addWeighted(图1,权重1, 图2, 权重2, gamma修正系数, dst可选参数, dtype可选参数)
|
||
|
||
h, w = inputimage.shape[:2]
|
||
result = np.zeros([h, w * 2], dtype=inputimage.dtype)
|
||
result[0:h, 0:w] = inputimage
|
||
result[0:h, w:2 * w] = usm
|
||
# cv.putText(图像名,标题,(x坐标,y坐标),字体,字的大小,颜色,字的粗细)
|
||
return usm
|
||
def HighPassFilter(self, src):
|
||
# 傅立叶变化
|
||
src_dft = cv2.dft(np.float32(src), flags=cv2.DFT_COMPLEX_OUTPUT)
|
||
# 将图片中心从左上角移到中心
|
||
src_dft_shift = np.fft.fftshift(src_dft)
|
||
|
||
# 制作掩膜令中心为0,后面才能过滤掉中心低频
|
||
rows, cols = src.shape
|
||
crow, ccol = int(rows / 2), int(cols / 2)
|
||
mask = np.ones((rows, cols, 2), np.uint8)
|
||
mask_size = 3
|
||
mask[crow - mask_size:crow + mask_size, ccol - mask_size:ccol + mask_size] = 0
|
||
|
||
# 用掩膜对图像进行处理
|
||
src_dft_shift_over = src_dft_shift * mask
|
||
|
||
# 将中心移回左上角
|
||
src_dft_shift_over_ishift = np.fft.ifftshift(src_dft_shift_over)
|
||
|
||
# 傅立叶逆变换
|
||
src_dft_shift_over_ishift_idft = cv2.idft(src_dft_shift_over_ishift)
|
||
|
||
# 后续操作,将矢量转换成标量,并映射到合理范围之内
|
||
src_dft_shift_over_ishift_idft = cv2.magnitude(src_dft_shift_over_ishift_idft[:, :, 0],
|
||
src_dft_shift_over_ishift_idft[:, :, 1])
|
||
src_dft_shift_over_ishift_idft = np.abs(src_dft_shift_over_ishift_idft)
|
||
src_dft_shift_over_ishift_idft = (src_dft_shift_over_ishift_idft - np.amin(src_dft_shift_over_ishift_idft)) / (
|
||
np.amax(src_dft_shift_over_ishift_idft) - np.amin(src_dft_shift_over_ishift_idft))
|
||
# src_dft_shift_over_ishift_idft = cv2.normalize(src_dft_shift_over_ishift_idft, 0, 255, cv2.NORM_MINMAX)
|
||
# print(src_dft_shift_over_ishift_idft)
|
||
return (src_dft_shift_over_ishift_idft * 255).astype('uint8')
|
||
def FindMaxContours_For_torxtamperproof(self, contours, size=100):
|
||
try:
|
||
# 計算輪廓面積
|
||
area = [cv2.contourArea(contour) for contour in contours]
|
||
# 找到前三大的面積的索引
|
||
max_indices = np.argsort(np.array(area))[::-1][:3]
|
||
# 取得前三大的輪廓
|
||
max_contours = [contours[i] for i in max_indices if cv2.contourArea(contours[i]) > 100]
|
||
# 計算每個輪廓的重心
|
||
centroids = []
|
||
for contour in max_contours:
|
||
moments = cv2.moments(contour)
|
||
# 檢查面積是否為非零值,避免除以零的情況
|
||
if moments["m00"] != 0:
|
||
cx = moments["m10"] / moments["m00"]
|
||
cy = moments["m01"] / moments["m00"]
|
||
centroids.append(np.array([cx, cy]))
|
||
# 計算每個輪廓的重心距離影像中心的距離
|
||
center = np.array([size / 2, size / 2]) # 替換成你的影像寬高
|
||
distances = [np.linalg.norm(centroid - center) for centroid in centroids]
|
||
# 找到最近的輪廓索引
|
||
closest_idx = np.argmin(np.array(distances))
|
||
# 找到最近的輪廓在前三大輪廓中的索引
|
||
final_idx = max_indices[closest_idx]
|
||
except Exception as e:
|
||
# self.dev_logger.error(f'FindMaxContours_For_torxtamperproof {e}')
|
||
final_idx = -1
|
||
print(f'FindMaxContours_For_torxtamperproof --> {e}')
|
||
return final_idx
|
||
def FindMaxContours_For_Other(self, contours, size=100):
|
||
try:
|
||
# 計算輪廓面積
|
||
area = [cv2.contourArea(contour) for contour in contours]
|
||
# 找到前三大的面積的索引
|
||
max_indices = np.argsort(np.array(area))[::-1][:6]
|
||
# 取得前三大的輪廓
|
||
max_contours = [contours[i] for i in max_indices if cv2.contourArea(contours[i]) > 20]
|
||
# 計算每個輪廓的重心
|
||
centroids = []
|
||
for contour in max_contours:
|
||
moments = cv2.moments(contour)
|
||
# 檢查面積是否為非零值,避免除以零的情況
|
||
if moments["m00"] != 0:
|
||
cx = moments["m10"] / moments["m00"]
|
||
cy = moments["m01"] / moments["m00"]
|
||
centroids.append(np.array([cx, cy]))
|
||
|
||
# 計算每個輪廓的重心距離影像中心的距離
|
||
center = np.array([size / 2, size / 2]) # 替換成你的影像寬高
|
||
distances = [np.linalg.norm(centroid - center) for centroid in centroids]
|
||
|
||
# 找到最近的輪廓索引
|
||
closest_idx = np.argmin(np.array(distances))
|
||
|
||
# 找到最近的輪廓在前三大輪廓中的索引
|
||
final_idx = max_indices[closest_idx]
|
||
|
||
except Exception as e:
|
||
# self.dev_logger.error(f'FindMaxContours_For_Other {e}')
|
||
final_idx = -1
|
||
print(f'FindMaxContours_For_Other --> {e}')
|
||
|
||
return final_idx
|
||
def FindMaxContours_For_hextamperproof(self, contours, size=20):
|
||
try:
|
||
# 計算輪廓面積
|
||
area = [cv2.contourArea(contour) for contour in contours]
|
||
# 找到前三大的面積的索引
|
||
max_indices = np.argsort(np.array(area))[::-1][:3]
|
||
|
||
# 取得前三大的輪廓
|
||
max_contours = [i for i in max_indices if cv2.contourArea(contours[i]) > 6]
|
||
|
||
for max_i in max_contours:
|
||
# 使用最小外接圓擬合輪廓
|
||
(x, y), radius = cv2.minEnclosingCircle(contours[max_i])
|
||
# print(radius)
|
||
# 檢查直徑是否在指定範圍內
|
||
if radius <= 40 and area[max_i] > size:
|
||
return max_i
|
||
final_idx = max_contours[-1]
|
||
except Exception as e:
|
||
# self.dev_logger.error(f'FindMaxContours_For_hextamperproof {e}')
|
||
final_idx = -1
|
||
print(f'FindMaxContours_For_hextamperproof --> {e}')
|
||
|
||
return final_idx
|
||
def FindMaxContours(self, contours):
|
||
try:
|
||
area = []
|
||
for contour in contours:
|
||
area.append(cv2.contourArea(contour))
|
||
max_idx = np.argmax(np.array(area))
|
||
except Exception as e:
|
||
# self.dev_logger.error(f'FindMaxContours {e}')
|
||
max_idx = -1
|
||
print(f'FindMaxContours --> {e}')
|
||
return max_idx
|
||
def draw_min_rect(self, image, contours, max_idx):
|
||
aspect_ratio = 29
|
||
try:
|
||
if max_idx > -1:
|
||
# Get the minimum bounding rectangle
|
||
rect = cv2.minAreaRect(contours[max_idx])
|
||
box = cv2.boxPoints(rect)
|
||
box = np.int0(box)
|
||
|
||
# Draw the rectangle on the image
|
||
# cv2.drawContours(image, [box], 0, (0, 255, 0), 2)
|
||
|
||
# Calculate and print the aspect ratio
|
||
width = np.linalg.norm(box[0] - box[1])
|
||
height = np.linalg.norm(box[1] - box[2])
|
||
if width > height:
|
||
temp = height
|
||
height = width
|
||
width = temp
|
||
|
||
if height == 0:
|
||
aspect_ratio = 1
|
||
else:
|
||
aspect_ratio = width / height
|
||
# print(f'Minimum Bounding Rectangle Aspect Ratio: {aspect_ratio}')
|
||
|
||
return aspect_ratio
|
||
else:
|
||
print('error --> draw_min_rect')
|
||
return 29
|
||
except Exception as e:
|
||
# self.dev_logger.error(f'draw_min_rect\n {e}')
|
||
return 29
|
||
|
||
|
||
class ROI_Data:
|
||
def __init__(self, image, y_start, y_stop, x_start, x_stop):
|
||
self.Image = image
|
||
self.Y_start = y_start
|
||
self.Y_stop = y_stop
|
||
self.X_start = x_start
|
||
self.X_stop = x_stop
|
||
|
||
class Image_and_Index:
|
||
def __init__(self, image, index):
|
||
self.Image = image
|
||
self.Image_Index = index
|
||
|
||
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 |