日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

YOLOv5实战之PCB板缺陷检测

發(fā)布時(shí)間:2024/1/1 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 YOLOv5实战之PCB板缺陷检测 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在前面的文章中已經(jīng)詳細(xì)介紹了在本機(jī)上安裝YOLOv5的教程,安裝YOLOv5可參考前面的文章YOLOv5訓(xùn)練自己的數(shù)據(jù)集(超詳細(xì))https://blog.csdn.net/qq_40716944/article/details/118188085https://blog.csdn.net/qq_40716944/article/details/118188085

?

目錄

一、數(shù)據(jù)集介紹

二、構(gòu)建訓(xùn)練數(shù)據(jù)集?

1、先構(gòu)建數(shù)據(jù)集文件夾

2、數(shù)據(jù)集格式轉(zhuǎn)換

3、訓(xùn)練集劃分代碼

4、生成yolo格式的標(biāo)簽

三、修改配置文件

1、數(shù)據(jù)配置文件

?2、網(wǎng)絡(luò)參數(shù)修改

3、trian.py修改

四、訓(xùn)練及測(cè)試

1、訓(xùn)練

2、測(cè)試


一、數(shù)據(jù)集介紹

? ? ? ? 本教程主要是利用YOLOv5算法實(shí)現(xiàn)對(duì)PCB板上的缺陷進(jìn)行檢測(cè)識(shí)別。使用的DeepPCB缺陷數(shù)據(jù)集中的所有圖像都是從線性掃描CCD獲得的,分辨率約為每1毫米48個(gè)像素,以上述方式從采樣圖像中手動(dòng)檢查,測(cè)試圖像的原始大小約為16k x 16k像素,?然后將它們裁剪成許多大小為640 x 640的子圖像,共1500張圖片,DeepPCB數(shù)據(jù)集中的部分圖片如下圖所示。PCB面板數(shù)據(jù)集已經(jīng)上傳,可以在此下載https://download.csdn.net/download/qq_40716944/24875013https://download.csdn.net/download/qq_40716944/24875013

?????????對(duì)于測(cè)試圖像中的每個(gè)缺陷,我們使用軸對(duì)齊的邊界框和一個(gè)類ID。 如上所示,我們標(biāo)注了六種常見(jiàn)的PCB缺陷類型:open、short、mousebite、spur、pin-hole、spur。 由于實(shí)際測(cè)試圖像中只有少數(shù)缺陷,我們根據(jù) PCB 缺陷模式在每個(gè)測(cè)試圖像上手動(dòng)論證一些人工缺陷,這導(dǎo)致每個(gè)640 x 640圖像中大約有3到12個(gè)缺陷。PCB缺陷數(shù)如下圖所示。 我們將1000?張圖像作為訓(xùn)練集,剩下的作為測(cè)試集。

二、構(gòu)建訓(xùn)練數(shù)據(jù)集?

1、先構(gòu)建數(shù)據(jù)集文件夾

? ? ? ? 本人按照VOC格式創(chuàng)建數(shù)據(jù)集,具體格式如下:

├── data │ ├── xml 進(jìn)行 detection 任務(wù)時(shí)的標(biāo)簽文件,xml 形式,文件名與圖片名一一對(duì)應(yīng) │ ├── image 存放.jpg 格式的圖片文件 │ ├── label 存放label標(biāo)注信息的txt文件,與圖片一一對(duì)應(yīng) │ ├── txt 存放原始標(biāo)注信息,x1,y1,x2,y2,type ├── dataSet(train,val,test建議按照8:1:1比例劃分) │ ├── train.txt 寫(xiě)著用于訓(xùn)練的圖片名稱 │ ├── val.txt 寫(xiě)著用于驗(yàn)證的圖片名稱 │ ├── trainval.txt train與val的合集 │ ├── test.txt 寫(xiě)著用于測(cè)試的圖片名稱

2、數(shù)據(jù)集格式轉(zhuǎn)換

????????原始的標(biāo)注信息是保存成txt文件,txt文件里面的每一行都包含一個(gè)標(biāo)注信息,格式為x1,y1,x2,y2,type,這里 (x1,y1) 和 (x2,y2) 是缺陷邊界框的左上角和右下角,type是匹配后的整數(shù) ID:0-background、1-open、2-short、3-mousebite、4-spur、5-copper、6-pin-hole。通過(guò)一下代碼進(jìn)行轉(zhuǎn)換:

import os import cv2 import time from xml.dom import minidomname_dict = {'0': 'background', '1': 'open', '2': 'short','3': 'mousebite', '4': 'spur', '5': 'copper', '6': 'pin-hole'}def transfer_to_xml(pic, txt, file_name,xml_save_path):if not os.path.exists(xml_save_path):os.makedirs(xml_save_path,exist_ok=True)img = cv2.imread(pic)img_w = img.shape[1]img_h = img.shape[0]img_d = img.shape[2]doc = minidom.Document()annotation = doc.createElement("annotation")doc.appendChild(annotation)folder = doc.createElement('folder')folder.appendChild(doc.createTextNode('visdrone'))annotation.appendChild(folder)filename = doc.createElement('filename')filename.appendChild(doc.createTextNode(file_name))annotation.appendChild(filename)source = doc.createElement('source')database = doc.createElement('database')database.appendChild(doc.createTextNode("Unknown"))source.appendChild(database)annotation.appendChild(source)size = doc.createElement('size')width = doc.createElement('width')width.appendChild(doc.createTextNode(str(img_w)))size.appendChild(width)height = doc.createElement('height')height.appendChild(doc.createTextNode(str(img_h)))size.appendChild(height)depth = doc.createElement('depth')depth.appendChild(doc.createTextNode(str(img_d)))size.appendChild(depth)annotation.appendChild(size)segmented = doc.createElement('segmented')segmented.appendChild(doc.createTextNode("0"))annotation.appendChild(segmented)with open(txt, 'r') as f:lines = [f.readlines()]for line in lines:for boxes in line:box = boxes.strip('\n')box = box.split(" ")x_min = box[0]y_min = box[1]x_max = box[2]y_max = box[3]object_name = name_dict[box[4]]if object_name != "background":object = doc.createElement('object')nm = doc.createElement('name')nm.appendChild(doc.createTextNode(object_name))object.appendChild(nm)pose = doc.createElement('pose')pose.appendChild(doc.createTextNode("Unspecified"))object.appendChild(pose)truncated = doc.createElement('truncated')truncated.appendChild(doc.createTextNode("1"))object.appendChild(truncated)difficult = doc.createElement('difficult')difficult.appendChild(doc.createTextNode("0"))object.appendChild(difficult)bndbox = doc.createElement('bndbox')xmin = doc.createElement('xmin')xmin.appendChild(doc.createTextNode(x_min))bndbox.appendChild(xmin)ymin = doc.createElement('ymin')ymin.appendChild(doc.createTextNode(y_min))bndbox.appendChild(ymin)xmax = doc.createElement('xmax')xmax.appendChild(doc.createTextNode(str(x_max)))bndbox.appendChild(xmax)ymax = doc.createElement('ymax')ymax.appendChild(doc.createTextNode(str(y_max)))bndbox.appendChild(ymax)object.appendChild(bndbox)annotation.appendChild(object)with open(os.path.join(xml_save_path, file_name + '.xml'), 'w') as x:x.write(doc.toprettyxml())x.close()f.close()if __name__ == '__main__':t = time.time()print('Transfer .txt to .xml...ing....')txt_folder = 'data/PCBDatasets/txt'txt_file = os.listdir(txt_folder)img_folder = 'data/PCBDatasets/image'xml_save_path = 'data/PCBDatasets/xml/'for txt in txt_file:txt_full_path = os.path.join(txt_folder, txt)img_full_path = os.path.join(img_folder, txt.split('.')[0] + '.jpg')try:transfer_to_xml(img_full_path, txt_full_path, txt.split('.')[0],xml_save_path)except Exception as e:print(e)print("Transfer .txt to .XML sucessed. costed: {:.3f}s...".format(time.time() - t))

3、訓(xùn)練集劃分代碼

????????主要是將數(shù)據(jù)集分類成訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集,默認(rèn)train,val,test按照比例進(jìn)行隨機(jī)分類,運(yùn)行后dataSet文件夾中會(huì)出現(xiàn)四個(gè)文件,主要是生成的訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集的圖片名稱,如下圖。同時(shí)data目錄下也會(huì)出現(xiàn)這四個(gè)文件,內(nèi)容是訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集的圖片路徑。

import os import randomtrainval_percent = 0.9 train_percent = 0.9 xmlfilepath = 'data/PCBDatasets/xml/' txtsavepath = 'data/PCBDatasets/dataSet/' total_xml = os.listdir(xmlfilepath)num = len(total_xml) list = range(num) tv = int(num * trainval_percent) tr = int(tv * train_percent) trainval = random.sample(list, tv) train = random.sample(trainval, tr)ftrainval = open('data/PCBDatasets/dataSet/trainval.txt', 'w') ftest = open('data/PCBDatasets/dataSet/test.txt', 'w') ftrain = open('data/PCBDatasets/dataSet/train.txt', 'w') fval = open('data/PCBDatasets/dataSet/val.txt', 'w')for i in list:name = total_xml[i][:-4] + '\n'if i in trainval:ftrainval.write(name)if i in train:ftrain.write(name)else:fval.write(name)else:ftest.write(name)ftrainval.close() ftrain.close() fval.close() ftest.close()

4、生成yolo格式的標(biāo)簽

????????主要是將圖片數(shù)據(jù)集標(biāo)注后的xml文件中的標(biāo)注信息讀取出來(lái)并寫(xiě)入txt文件,運(yùn)行后在label文件夾中出現(xiàn)所有圖片數(shù)據(jù)集的標(biāo)注信息

# xml解析包import xml.etree.ElementTree as ET import pickle import os# os.listdir() 方法用于返回指定的文件夾包含的文件或文件夾的名字的列表from os import listdir, getcwd from os.path import joinsets = ['train', 'test', 'val'] classes = ['open', 'short','mousebite','spur', 'copper', 'pin-hole']# 進(jìn)行歸一化操作def convert(size, box): # size:(原圖w,原圖h) , box:(xmin,xmax,ymin,ymax)dw = 1./size[0] # 1/wdh = 1./size[1] # 1/hx = (box[0] + box[1])/2.0 # 物體在圖中的中心點(diǎn)x坐標(biāo)y = (box[2] + box[3])/2.0 # 物體在圖中的中心點(diǎn)y坐標(biāo)w = box[1] - box[0] # 物體實(shí)際像素寬度h = box[3] - box[2] # 物體實(shí)際像素高度x = x*dw # 物體中心點(diǎn)x的坐標(biāo)比(相當(dāng)于 x/原圖w)w = w*dw # 物體寬度的寬度比(相當(dāng)于 w/原圖w)y = y*dh # 物體中心點(diǎn)y的坐標(biāo)比(相當(dāng)于 y/原圖h)h = h*dh # 物體寬度的寬度比(相當(dāng)于 h/原圖h)return (x, y, w, h) # 返回 相對(duì)于原圖的物體中心點(diǎn)的x坐標(biāo)比,y坐標(biāo)比,寬度比,高度比,取值范圍[0-1]def convert_annotation(image_id):'''將對(duì)應(yīng)文件名的xml文件轉(zhuǎn)化為label文件,xml文件包含了對(duì)應(yīng)的bunding框以及圖片長(zhǎng)款大小等信息,通過(guò)對(duì)其解析,然后進(jìn)行歸一化最終讀到label文件中去,也就是說(shuō)一張圖片文件對(duì)應(yīng)一個(gè)xml文件,然后通過(guò)解析和歸一化,能夠?qū)?duì)應(yīng)的信息保存到唯一一個(gè)label文件中去labal文件中的格式:calss x y w h  同時(shí),一張圖片對(duì)應(yīng)的類別有多個(gè),所以對(duì)應(yīng)的bounding的信息也有多個(gè)'''# 對(duì)應(yīng)的通過(guò)year 找到相應(yīng)的文件夾,并且打開(kāi)相應(yīng)image_id的xml文件,其對(duì)應(yīng)bund文件in_file = open('data/PCBDatasets/xml/%s.xml' % (image_id), encoding='utf-8')# 準(zhǔn)備在對(duì)應(yīng)的image_id 中寫(xiě)入對(duì)應(yīng)的label,分別為# <object-class> <x> <y> <width> <height>out_file = open('data/PCBDatasets/label/%s.txt' % (image_id), 'w', encoding='utf-8')# 解析xml文件tree = ET.parse(in_file)# 獲得對(duì)應(yīng)的鍵值對(duì)root = tree.getroot()# 獲得圖片的尺寸大小size = root.find('size')# 如果xml內(nèi)的標(biāo)記為空,增加判斷條件if size != None:# 獲得寬w = int(size.find('width').text)# 獲得高h(yuǎn) = int(size.find('height').text)# 遍歷目標(biāo)objfor obj in root.iter('object'):# 獲得difficult ??difficult = obj.find('difficult').text# 獲得類別 =string 類型cls = obj.find('name').text# 如果類別不是對(duì)應(yīng)在我們預(yù)定好的class文件中,或difficult==1則跳過(guò)if cls not in classes or int(difficult) == 1:continue# 通過(guò)類別名稱找到idcls_id = classes.index(cls)# 找到bndbox 對(duì)象xmlbox = obj.find('bndbox')# 獲取對(duì)應(yīng)的bndbox的數(shù)組 = ['xmin','xmax','ymin','ymax']b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),float(xmlbox.find('ymax').text))print(image_id, cls, b)# 帶入進(jìn)行歸一化操作# w = 寬, h = 高, b= bndbox的數(shù)組 = ['xmin','xmax','ymin','ymax']bb = convert((w, h), b)# bb 對(duì)應(yīng)的是歸一化后的(x,y,w,h)# 生成 calss x y w h 在label文件中out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')# 返回當(dāng)前工作目錄wd = getcwd() print(wd)for image_set in sets:'''對(duì)所有的文件數(shù)據(jù)集進(jìn)行遍歷做了兩個(gè)工作:1.將所有圖片文件都遍歷一遍,并且將其所有的全路徑都寫(xiě)在對(duì)應(yīng)的txt文件中去,方便定位2.同時(shí)對(duì)所有的圖片文件進(jìn)行解析和轉(zhuǎn)化,將其對(duì)應(yīng)的bundingbox 以及類別的信息全部解析寫(xiě)到label 文件中去最后再通過(guò)直接讀取文件,就能找到對(duì)應(yīng)的label 信息'''# 先找labels文件夾如果不存在則創(chuàng)建if not os.path.exists('data/PCBDatasets/labels/'):os.makedirs('data/PCBDatasets/labels/')# 讀取在ImageSets/Main 中的train、test..等文件的內(nèi)容# 包含對(duì)應(yīng)的文件名稱image_ids = open('data/PCBDatasets/dataSet/%s.txt' % (image_set)).read().strip().split()list_file = open('data/PCBDatasets/%s.txt' % (image_set), 'w')# 將對(duì)應(yīng)的文件_id以及全路徑寫(xiě)進(jìn)去并換行for image_id in image_ids:list_file.write('data/PCBDatasets/image/%s.jpg\n' % (image_id))# 調(diào)用 year = 年份 image_id = 對(duì)應(yīng)的文件名_idconvert_annotation(image_id)# 關(guān)閉文件list_file.close()

label文件夾中某文件內(nèi)容如下:

三、修改配置文件

1、數(shù)據(jù)配置文件

????????首先需要在/yolov5-master/data文件夾中,新建一個(gè)PCBDetect.yaml文件,內(nèi)容設(shè)置如下:

train: data/PCBDatasets/dataSet/train.txt val: data/PCBDatasets/dataSet/val.txt test: data/PCBDatasets/dataSet/test.txtnc: 6names: ['copper', 'mousebite', 'open', 'pin-hole', 'short', 'spur']

?2、網(wǎng)絡(luò)參數(shù)修改

????????對(duì)yolov5-master/model文件夾中,對(duì)yolov5x.yaml(根據(jù)自己選擇的模型而定)文件內(nèi)容修改。

3、trian.py修改

????????主要用到的幾個(gè)參數(shù):–weights,–cfg,–data,–epochs,–batch-size,–img-size,–project,-workers

????????重點(diǎn)注意:–weights,–cfg,–data,其他的默認(rèn)即可(batch_size,workers根據(jù)自己電腦屬性進(jìn)行設(shè)置)。

四、訓(xùn)練及測(cè)試

1、訓(xùn)練

????????在完成上述所有的操作之后,就可以進(jìn)行訓(xùn)練,在命令窗口輸入python train.py即可以進(jìn)行訓(xùn)練。

2、測(cè)試

????????在訓(xùn)練完成后可以利用測(cè)試集對(duì)訓(xùn)練后的模型進(jìn)行測(cè)試,利用val.py文件進(jìn)行測(cè)試,主要修改一下地方:

????????測(cè)試完成后會(huì)輸出map、precision、recall等指標(biāo),具體如下圖所示:

?????????P-R曲線如下圖所示:

????????同時(shí)也可以利用detect.py文件對(duì)測(cè)試集進(jìn)行測(cè)試,將檢測(cè)后的框繪制在圖像上,部分測(cè)試結(jié)果如下圖所示:

總結(jié)

以上是生活随笔為你收集整理的YOLOv5实战之PCB板缺陷检测的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。