工業(yè)視覺缺陷檢測是一種利用計算機(jī)視覺技術(shù)跛蛋,對工業(yè)制品進(jìn)行自動化檢測,以識別和分類可能存在的缺陷的方法痊硕。它是現(xiàn)代工業(yè)生產(chǎn)中的重要環(huán)節(jié)赊级,可以大大提高生產(chǎn)效率,降低產(chǎn)品缺陷率岔绸,提高產(chǎn)品質(zhì)量理逊。
成像質(zhì)量是根本,好的算法可以錦上添花
該項目為醫(yī)療注射器缺陷檢測盒揉,要求檢觀出汗射器是否有質(zhì)量缺路(缺件或者多件)晋被,檢測對象分別為,膠暴刚盈、推桿承部針尾品羡洛、針嘴、媒口扁掸、小較暴翘县,產(chǎn)品如下圖所示擺放最域,針對歪嘴情況單獨來增加一個類作為檢測項。
通過對樣本進(jìn)行分析锈麸,注射器擺放的位置略有差異镀脂,采用目標(biāo)檢測來檢測各個部件是否存在比較合適,最后綜合判斷每個注射器的最終結(jié)果忘伞,如果檢測某個注射器出現(xiàn)歪嘴或者缺件的直接輸出對應(yīng)的NG信號薄翅,方便剔除不合格產(chǎn)品。
采集圖片:使用相機(jī)采圖工具或者專用軟件對缺陷產(chǎn)品進(jìn)行采圖氓奈,本項目采集圖片1000張左右翘魄,為了模擬小樣本訓(xùn)練的效果,實際只使用200張左右舀奶。
標(biāo)注:標(biāo)注工程師按照要求將目標(biāo)檢測項分為7類暑竟,分別是膠塞、推桿尾部育勺、針尾部.
針嘴但荤、歪嘴、螺口涧至、小膠塞腹躁。
訓(xùn)練:使用改進(jìn)的yolov8進(jìn)行訓(xùn)練得到模型。
模型上線:模型轉(zhuǎn)換南蓬、量化等纺非,使模型滿足部署平臺的上線要求,這里使用Aidlux進(jìn)行部署赘方。
相機(jī):工業(yè)高清黑白相機(jī)600w
光源:紅外平板光源烧颖,背面補(bǔ)光
鏡頭:工業(yè)高清FA鏡頭35mm
算法:yolov8
部署:工控機(jī)顯卡RTX206012G
界面及通信:WPF或者C#或者OT (這里不做討論)
選擇yolov8模型的理由:集大成者之作,包括分類蒜焊、實例分割倒信、目標(biāo)檢測、關(guān)鍵點檢測泳梆、目標(biāo)跟蹤等鳖悠,更多的功能更快的推理速度、更高的精度优妙、更加易于訓(xùn)練和調(diào)整支持更多的平臺乘综、全新的SOTA模型
該項目使用volov8的目標(biāo)檢測,對該模型進(jìn)行兩點改進(jìn)在head中加入slimNeck
在優(yōu)化中加入Siou
改進(jìn)1:yolov8模型改進(jìn): slicNeck
將neck中的4個CSPLayer2Conv全部更換為VoVGSCSPC套硼。
相比與C2f卡辰,VoVGSCSPC在保持性能的同時具更少的有參數(shù)量,輕量化效果明顯。
改進(jìn)2:
yolov8模型改進(jìn): slicNeckslimNeck之GSConv
slimNeck的作者提出了一種新方法GSConv來減輕模型復(fù)雜度九妈,保持準(zhǔn)確性反砌。GSConv可以更好地平衡模型的準(zhǔn)確性和速度。并且萌朱,提供了一種設(shè)計范式Slim-Neck宴树,以實現(xiàn)檢測器更高的計算成本效益。實驗過程中晶疼,與原始網(wǎng)絡(luò)相比改進(jìn)方法獲得了最優(yōu)秀的檢測結(jié)果酒贬。
改進(jìn)3:
yolov8模型改進(jìn): slicNeckslimNeck之VoVGSCSPC
P1--P2--P3,依次增加殘差連接層的個數(shù)翠霍,使得到的特征層的維度更多锭吨,獲得抽象的語義特征信息越豐富。
改進(jìn)4:
yolov8模型改進(jìn): SIoU
源代碼修改:
進(jìn)入到ultralyticsyololutils metrics,py中寒匙,76行開始修改bbox_iou方法零如,增加和修改SIoU,EloU,Focal,alpha,gamma相關(guān)的代碼.
進(jìn)入到ultralyticsyoloutils loss.py中,43行開始修改bbox_iou, 并注釋掉以前的loss_iou蒋情,增加帶Fcal的loss_iouo
進(jìn)入到ultralyticslyoloutilsltalpy中埠况,157行修改bbox_iouo
注射器數(shù)據(jù)集:139張訓(xùn)練數(shù)據(jù)集,46張驗證集
訓(xùn)練方式:基于yolov8預(yù)訓(xùn)練權(quán)重進(jìn)行訓(xùn)練
新建或者拷貝對應(yīng)的yaml文件棵癣,設(shè)置nc=7,如果是自己的數(shù)據(jù)集夺衍,按照實際類別進(jìn)行設(shè)置狈谊。
訓(xùn)練yolov8n模型
使用main.py文件,訓(xùn)練模型或者轉(zhuǎn)換為onnx模型加載自己的yaml文件沟沙,再加載預(yù)訓(xùn)練模型河劝,將預(yù)訓(xùn)練權(quán)重移植到新模型中,這樣就可以開始訓(xùn)練yolov8模型了矛紫。
if __name__ == '__main__':
? ? mode="predict"
? ? if mode=="train":
? ? ? ? model=YOLO("E:\\Aidlux8\\2\\yolov8-aidlux\\yolov8-main\\yolov8n.yaml")
? ? ? ? model=YOLO("E:\\Aidlux8\\2\\yolov8-aidlux\\yolov8-main\\yolov8n.pt")
? ? ? ? # model.train(**{'cfg':'D:\\AI\\YOLO\\yolov8-main\\ultralytics\\yolo\\cfg\\keypoints.yaml'})
? ? ? ? model.train(data='ultralytics\\datasets\\keypoint.yaml',epochs=100,device=0,batch=4,workers=6)
? ? ? ? # path = model.export(format="onnx")
? ? if mode=="onnx" :
? ? ? ? #D:\\IROnnx\\best.pt
? ? ? ? model = YOLO('E:\\Aidlux8\\2\\yolov8-aidlux\\yolov8-main\\runs\\detect\\train8\\weights\\best.pt')
? ? ? ? # model = YOLO('D:\\AI\\Unet\\unet-pytorch\\logs\\best_epoch_weights.pth')
? ? ? ? model.export(format="onnx",opset=11,simplify=True)
? ? ? ? # path = model.export(format="onnx",opset=13,half=True,simplify=True)
? ? ? ? # path = model.export(format="onnx",opset=13,half=True,simplify=True)
訓(xùn)練過程:
將預(yù)訓(xùn)練權(quán)重的加載到了我們自己的模型中赎瞎,經(jīng)過3個batchsize,模型就開始收斂颊咬。
Aidlux平臺介紹
Aidlux安裝和環(huán)境配置
特點o安卓手機(jī)app市場下載并安裝AidLux應(yīng)用务甥。安裝包大小為1.2g打開AidLux,配置各種權(quán)限喳篇,手機(jī)-設(shè)置-關(guān)于手機(jī)-多次點擊系統(tǒng)版本號敞临,打開開發(fā)者模式重啟AidLux,選擇登陸或者免注冊登陸麸澜,應(yīng)用系統(tǒng)第一次打開需要加載AidLux挺尿,等待進(jìn)度條完成手機(jī)端進(jìn)入AidLux界面后,查看手機(jī)端桌面Cloud ip藍(lán)色云朵圖標(biāo),獲得ip和端口號PC端瀏覽器中輸入ip地址和端口號编矾,登錄界面為root用戶熟史,輸入aidlux(默認(rèn)密碼)登陸進(jìn)入到AidLux桌面打開終端更新和安裝所需工具包,默認(rèn)已安裝aidlite_gpu,也可以更新窄俏。
安裝vscode:
打開應(yīng)用中心蹂匹,選擇Linux-Aid源-vscode進(jìn)行安裝
進(jìn)入已安裝-點擊vscode-點擊添加到桌面-桌面上顯示vscode
可以使用直接在設(shè)備里直接調(diào)試運行vscode,也可以使用ssh連接vscode進(jìn)行遠(yuǎn)程調(diào)試裆操。
基于Aidlux的yolov8模型轉(zhuǎn)換
打開網(wǎng)站: http://aimo.aidlux.com/
輸入試用賬號和密碼:
賬號:AIMOTC001怒详,密碼:AIMOTCO01
選擇對應(yīng)的原模型(.onnx)->選擇目標(biāo)平臺(tensorflowlite)->參數(shù)設(shè)置默認(rèn)->轉(zhuǎn)換結(jié)果->下載模型
基于tflite的yolov8模型部署
images文件夾:推理圖像樣本
onnx原模型:yolov8_slimneck_SIOU.onnx
tflite: tflite模型和推理的py文件
手機(jī)設(shè)備打開AidLux
PC網(wǎng)頁登錄AidLux,默認(rèn)用戶為root踪区,密碼為aidlux將images昆烁、tflite模型文件以及tflite.py文件放入到home目錄中
打開vscode,打開tflite.py文件缎岗,右鍵在終端運行静尼,可在result中查找推理的結(jié)果
最后的推理源碼如下:
import aidlite_gpu
import cv2
from cvs import *
import numpy as np
import os
import time
# import ?matplotlib.pyplot as plt
model_path = "/home/yolov8_slimneck_SIOU_save_path_tflite/yolov8_slimneck_SIOU_fp32.tflite"
image_path = "/home/test"
NUMS_CLASS = 7
confThresh = 0.3
NmsThresh = 0.45
#輸入格式 (8400,11)
def postProcess(pres, confThresh, NmsThresh):
? ? boxes_out = []
? ? scores_out = []
? ? class_out = []
? ? for pred in pres:
? ? ? ? pred_class = pred[4:]
? ? ? ? box_ = pred[0:4]
? ? ? ? # pred_class=(pred_class-min(pred_class))/(max(pred_class)-min(pred_class))
? ? ? ? class_index = np.argmax(pred_class)
? ? ? ? if pred_class[class_index] > 0.3:
? ? ? ? ? ? # box=np.array([round(pred[2]-0.5*pred[0]),round(pred[3]-0.5*pred[1]),round(pred[0]),round(pred[1])])
? ? ? ? ? ? box_ = pred[0:4] ?# w,h,xc,yc
? ? ? ? ? ? box = np.array([round((pred[2] / 2 - pred[0])), round((pred[3] / 2 - pred[1])), round(pred[0] * 2),
? ? ? ? ? ? ? ? ? ? ? ? ? ? round(pred[1] * 2)])
? ? ? ? ? ? boxes_out.append(box)
? ? ? ? ? ? score = pred_class[class_index]
? ? ? ? ? ? scores_out.append(score)
? ? ? ? ? ? class_out.append(class_index)
? ? result_boxes = cv2.dnn.NMSBoxes(boxes_out, np.array(scores_out), confThresh, NmsThresh)
? ? # detections=[]
? ? boxes = []
? ? scores = []
? ? classes = []
? ? for result_box in result_boxes:
? ? ? ? index = int(result_box)
? ? ? ? box = boxes_out[index]
? ? ? ? score = scores_out[index]
? ? ? ? class_type = class_out[index]
? ? ? ? boxes.append(box)
? ? ? ? scores.append(score)
? ? ? ? classes.append(class_type)
? ? return boxes, scores, classes
def draw(img, xscale, yscale, boxes, scores, classes):
? ? width = img.shape[1]
? ? w1 = 1620
? ? w2 = 2350
? ? w3 = width
? ? S1 = []
? ? S2 = []
? ? S3 = []
? ? S1_res = [False for i in range(NUMS_CLASS)]
? ? S2_res = [False for i in range(NUMS_CLASS)]
? ? S3_res = [False for i in range(NUMS_CLASS)]
? ? S_res = [S1_res, S2_res, S3_res]
? ? img_ = img.copy()
#遍歷所有box,按照分割區(qū)域?qū)ox歸類
? ? for i in range(len(boxes)):
? ? ? ? # boxes=[x1,y1,w,h]
? ? ? ? box = boxes[i]
? ? ? ? score = scores[i]
? ? ? ? class_ = int(classes[i])
? ? ? ? # class_text=label[class_]
? ? ? ? # detect=[round(box[0]*xscale),round(box[1]*yscale),round((box[0]+box[2])*xscale),round((box[1]+box[3])*yscale)]
? ? ? ? detect = [round(box[0] * xscale), round(box[1] * yscale), round(box[0] * xscale + (box[2]) * xscale),
? ? ? ? ? ? ? ? ? round(box[1] * yscale + (box[3]) * yscale)]
? ? ? ? text = "{}:{:.2f}".format(label[class_], float(score))
? ? ? ? img_ = cv2.rectangle(img_, (detect[0], detect[1]), (detect[2], detect[3]), (0, 255, 0), 2)
? ? ? ? cv2.putText(img_, text, (detect[0], detect[1] + 10), cv2.FONT_HERSHEY_COMPLEX, 2, (0, 0, 255), 1)
#分割為三塊
? ? ? ? if (detect[0] <= w1):
? ? ? ? ? ? p1 = []
? ? ? ? ? ? p1.append(detect)
? ? ? ? ? ? p1.append(class_)
? ? ? ? ? ? p1.append(score)
? ? ? ? ? ? S1.append(p1)
? ? ? ? elif (w1 < detect[0] <= w2):
? ? ? ? ? ? p2 = []
? ? ? ? ? ? p2.append(detect)
? ? ? ? ? ? p2.append(class_)
? ? ? ? ? ? p2.append(score)
? ? ? ? ? ? S2.append(p2)
? ? ? ? elif (w2 < detect[0] <= w3):
? ? ? ? ? ? p3 = []
? ? ? ? ? ? p3.append(detect)
? ? ? ? ? ? p3.append(class_)
? ? ? ? ? ? p3.append(score)
? ? ? ? ? ? S3.append(p3)
#判斷每個分割圖像中的結(jié)果
? ? index = 0
? ? for S in [S1, S2, S3]:
? ? ? ? for i in range(len(S)):
? ? ? ? ? ? p1 = S[i]
? ? ? ? ? ? box_temp = p1[0]
? ? ? ? ? ? class_temp = p1[1]
? ? ? ? ? ? score_temp = p1[2]
? ? ? ? ? ? S_res[index][class_temp] = True
? ? ? ? index += 1
#最終分割輸出結(jié)果true or false
? ? S_out = [False, False, False]
? ? index_out = 0
? ? for s_r in S_res:
? ? ? ? c0 = s_r[0]
? ? ? ? c1 = s_r[1]
? ? ? ? c2 = s_r[2]
? ? ? ? c3 = s_r[3]
? ? ? ? c4 = s_r[4]
? ? ? ? c5 = s_r[5]
? ? ? ? c6 = s_r[6]
? ? ? ? if (c0 & c1 & c2 & c3 & (~c4) & (~c5) & (~c6)):
? ? ? ? ? ? S_out[index_out] = True
? ? ? ? elif (c0 & c1 & c2 & (~c3) & (~c4) & c5 & (~c6)):
? ? ? ? ? ? S_out[index_out] = True
? ? ? ? index_out += 1
#打印分割結(jié)果
? ? cv2.putText(img_, "OK" if S_out[0] == True else "NG", (w1 - 200, 100), cv2.FONT_HERSHEY_COMPLEX, 2, (255, 0, 0), 1)
? ? cv2.putText(img_, "OK" if S_out[1] == True else "NG", (w2 - 200, 100), cv2.FONT_HERSHEY_COMPLEX, 2, (255, 0, 0), 1)
? ? cv2.putText(img_, "OK" if S_out[2] == True else "NG", (w3 - 200, 100), cv2.FONT_HERSHEY_COMPLEX, 2, (255, 0, 0), 1)
? ? return img_
label = ["rubber stopper", "push rod tail", "needle tail", "mouth", "crooked mouth", "screw mouth", "small rubber plug"]
if __name__ == "__main__":
# 1.初始化aidlite類并創(chuàng)建aidlite對象
? ? aidlite = aidlite_gpu.aidlite()
? ? print("ok")
# 2.加載模型
? ? value = aidlite.ANNModel(model_path, [640 * 640 * 3 * 4], [8400 * 11 * 4], 4, 0)
? ? print("gpu:", value)
? ? # file_names=os.listdir(image_path)
? ? # root,dirs,files = os.walk(image_path)
? ? for root, dirs, files in os.walk(image_path):
? ? ? ? num = 0
? ? ? ? for file in files:
? ? ? ? ? ? file = os.path.join(root, file)
? ? ? ? ? ? frame = cv2.imread(file)
? ? ? ? ? ? x_scale = frame.shape[1] / 640
? ? ? ? ? ? y_scale = frame.shape[0] / 640
? ? ? ? ? ? img = cv2.resize(frame, (640, 640))
? ? ? ? ? ? # img_copy=img.co
? ? ? ? ? ? img = img / 255.0
? ? ? ? ? ? img = np.expand_dims(img, axis=0)
? ? ? ? ? ? img = img.astype(dtype=np.float32)
? ? ? ? ? ? print(img.shape)
# 3.傳入模型輸入數(shù)據(jù)
? ? ? ? ? ? aidlite.setInput_Float32(img)
# 4.執(zhí)行推理
? ? ? ? ? ? start = time.time()
? ? ? ? ? ? aidlite.invoke()
? ? ? ? ? ? end = time.time()
? ? ? ? ? ? timerValue = 1000 * (end - start)
? ? ? ? ? ? print("infer time(ms):{0}", timerValue)
# 5.獲取輸出
? ? ? ? ? ? pred = aidlite.getOutput_Float32(0)
? ? ? ? ? ? # print(pred.shape)
? ? ? ? ? ? pred = np.array(pred)
? ? ? ? ? ? print(pred.shape)
? ? ? ? ? ? pred = np.reshape(pred, (8400, 11))
? ? ? ? ? ? # pred=np.reshape(pred,(11,8400)).transpose()
? ? ? ? ? ? print(pred.shape) ?# shape=(8400,11)
# 6.后處理,解析輸出
? ? ? ? ? ? boxes, scores, classes = postProcess(pred, confThresh, NmsThresh)
# 7.繪制保存圖像
? ? ? ? ? ? ret_img = draw(frame, x_scale, y_scale, boxes, scores, classes)
? ? ? ? ? ? ret_img = ret_img[:, :, ::-1]
? ? ? ? ? ? num += 1
? ? ? ? ? ? image_file_name = "/home/result/res" + str(num) + ".jpg"
# 8.保存圖片
? ? ? ? ? ? cv2.imwrite(image_file_name, ret_img)
個人體會:
筆者是在Aidlux團(tuán)隊以及龍哥的訓(xùn)練營中學(xué)習(xí)而來传泊,期間龍哥區(qū)別以往的視頻課鼠渺,以一種更加直觀的方式展現(xiàn)出整個項目的流程與細(xì)節(jié)。不管是AI算法小白還是AI算法的老手都在這次訓(xùn)練營受益匪淺眷细。Aidlux工程實踐內(nèi)容全是干貨拦盹,同時過程也遇見了很多問題,但是龍哥和訓(xùn)練營的其他同學(xué)們都很認(rèn)真為其他學(xué)員解決溪椎,耐心輔導(dǎo)普舆,對我來言,剛剛接觸這一領(lǐng)域校读,以及Aidlux平臺的使用沼侣,讓我耳目一新。整個流程下歉秫,我已經(jīng)學(xué)會了如何在Aidlux進(jìn)行模型部署蛾洛,令我也感覺到成就感,在此特別感謝張子豪老師和Aidlux團(tuán)隊的貢獻(xiàn)雁芙,希望他們以后在AI算法開發(fā)的道路事業(yè)更加順利轧膘。
最后放上本次基于Aidlux平臺實現(xiàn)工業(yè)視覺缺陷檢測的效果視頻的地址。
https://www.bilibili.com/video/BV1GN4y197cD/