
簡介YOLOv8鋼材缺陷檢測完整方案面向工業質檢算法學習者和視覺項目開發者解決鋼材表面多類型缺陷的自動識別與可視化檢測問題。壓縮包共2000個文件包含346張標注圖像、1403個txt標簽及配套xml標注文件以及訓練好的pt權重、yaml配置、Python腳本、pyqt界面ui文件和訓練曲線pdf等整體約94.62MB目錄劃分清晰。檢測模型已訓練完成附帶PR曲線、Loss曲線等評估結果可直接用于圖片、視頻及攝像頭實時檢測場景。pyqt圖形界面提供檢測圖片、視頻和調用攝像頭的選擇項便于二次開發與演示。數據集采用labelimg標注jpg圖片與xml、txt雙格式標簽分離存放適合目標檢測任務訓練與驗證。已有605人學習下載適合具備一定深度學習基礎、希望快速落地YOLOv8缺陷檢測項目的工程師或研究者。1. 從產線質檢到桌面工具YOLOv8鋼材缺陷檢測為什么需要一套完整鏈路鋼材表面缺陷檢測是工業視覺里最典型的落地場景之一熱軋帶鋼、冷軋板卷、型鋼表面在連續生產過程中會出現裂紋、麻點、夾雜、氧化皮壓入等缺陷。傳統方案靠人工目檢效率低且容易漏檢而通用目標檢測模型直接套用在鋼材表面場景會遇到小目標占比高、缺陷形態長寬比極端、背景紋理干擾強這三個核心問題。YOLOv8在C2f結構和Anchor-Free檢測頭的設計上相比YOLOv5有更靈活的梯度流和更簡潔的解耦頭適合作為這個任務的基礎骨架。但模型訓練只是其中一環。實際項目中工程師需要一份整理好的數據集、一組能直接加載的缺陷檢測權重以及一個能讓現場操作人員不看命令行也能完成單張圖片和視頻檢測的圖形界面。把這三件事拼起來才是一條從模型訓練到產線試用的完整鏈路。這篇文章假設你已經有Python基礎不需要GPU服務器一臺帶有6GB以上顯存的消費級顯卡就能跑通全部流程。全文按數據集準備、權重訓練、Qt界面封裝、落地排錯的順序展開每一步都有可直接執行的命令和代碼。2. 鋼材缺陷檢測數據集的選型與預處理NEU-DET怎么變成YOLOv8能吃的格式2.1 公開數據集和自采數據的取舍缺樣本時先做哪幾件事鋼材缺陷檢測領域最常用的公開數據集是NEU-DET它來自東北大學包含熱軋帶鋼表面六大類缺陷rolled-in scale氧化鐵皮壓入、patches麻點、crazing裂紋、pitted surface凹坑、inclusion夾雜、scratches劃痕。每類樣本180張共1800張圖片分辨率200x200像素。這個數據集規模不大直接訓練容易過擬合而且單張圖片尺寸偏小和產線相機采集的2000萬像素工業圖像分布差異明顯。如果你手頭有現場采集數據我一般會建議先用NEU-DET跑通流程再用現場數據做微調。現場數據標注格式可能是VOC的XML也可能是MS COCO的JSON。YOLOv8官方訓練接口接受兩種格式一種是每張圖片對應一個同名TXT文件每一行是class_id x_center y_center width height坐標統一歸一化到0到1另一種是COCO格式的JSON通過參數formatcoco指定。無論原始格式是什么第一步都是統一轉換成YOLO的TXT格式。import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size).find(width).text) img_h int(root.find(size).find(height).text) txt_name os.path.splitext(os.path.basename(xml_path))[0] .txt out_path os.path.join(out_dir, txt_name) with open(out_path, w) as f: for obj in root.findall(object): class_name obj.find(name).text if class_name not in class_names: continue class_id class_names.index(class_name) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) x_center (x1 x2) / 2.0 / img_w y_center (y1 y2) / 2.0 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h f.write(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) class_names [crazing, inclusion, patches, pitted_surface, rolled-in_scale, scratches] # 對NEU-DET的每一張XML標注執行轉換 xml_dir data/NEU-DET/ANNOTATIONS txt_dir data/NEU-DET/labels os.makedirs(txt_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): convert_voc_to_yolo(os.path.join(xml_dir, xml_file), txt_dir, class_names)這段代碼包含一個關鍵處理細節NEU-DET原標注的類別名和YOLOv8的類別索引必須手動對齊比如rolled-in_scale包含連字符在類名列表里要嚴格一致。VOC坐標轉歸一化坐標的公式是固定的但要注意某些標注工具的坐標原點在左下角而圖像數組的坐標原點在左上角NEU-DET不存在這個問題自采數據建議先畫框驗證一張再批量轉換。2.2 缺陷檢測權重訓練前的數據集劃分與增強策略數據集劃分上NEU-DET的標準做法是隨機分配訓練集和驗證集比如訓練集占80%驗證集占20%。鋼材缺陷檢測和自然場景目標檢測有個重要區別相鄰缺陷在空間上高度相關同一塊鋼材上的缺陷類型往往有聚集性。如果按整圖隨機劃分可能出現同一批鋼材的圖像同時出現在訓練集和驗證集評估指標虛高。更好的做法是按鋼材母卷編號分組劃分這在NEU-DET上實現不了因為官方沒有提供圖像間的關聯信息但對自采數據一定要按生產線批次劃分。import os import random from collections import defaultdict def split_dataset_by_group(image_dir, label_dir, output_dir, train_ratio0.8): images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] groups defaultdict(list) for img_name in images: # 假設文件名格式為 steel_20240115_batch01_0001.jpg # 取前三段標識一個母卷批次 parts img_name.split(_) if len(parts) 3: group_key _.join(parts[:3]) else: group_key default groups[group_key].append(img_name) train_imgs, val_imgs [], [] for group_key, group_imgs in groups.items(): random.shuffle(group_imgs) split_idx int(len(group_imgs) * train_ratio) train_imgs.extend(group_imgs[:split_idx]) val_imgs.extend(group_imgs[split_idx:]) os.makedirs(os.path.join(output_dir, images, train), exist_okTrue) os.makedirs(os.path.join(output_dir, images, val), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, train), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, val), exist_okTrue) for img_name in train_imgs: os.system(fcp {os.path.join(image_dir, img_name)} {os.path.join(output_dir, images, train)}/) lbl img_name.replace(.jpg, .txt) if os.path.exists(os.path.join(label_dir, lbl)): os.system(fcp {os.path.join(label_dir, lbl)} {os.path.join(output_dir, labels, train)}/) # 驗證集同理 print(f訓練集 {len(train_imgs)} 張驗證集 {len(val_imgs)} 張)按批次劃分這段代碼用文件名提取分組鍵這個思路在生產項目里很實用。NEU-DET文件名是NEU-CLS-XXXX形式無法提取批次但如果你的自采圖像命名規則里包含了產線號、日期、軋制批次這行代碼可以直接復用。數據增強策略上鋼材表面缺陷有三個特殊情況需要注意。第一大多數缺陷是灰度紋理變化色相飽和度增強對模型沒有正向幫助反而可能引入噪聲所以hsv_h、hsv_s參數應該調低。第二缺陷長寬比極端比如劃痕可能橫向跨整個圖像寬度但高度只有幾個像素mosaic增強在拼接時會裁掉部分缺陷嚴重降低小缺陷的可見性。第三鋼材表面有周期性紋理fliplr水平翻轉會讓模型學習到左右對稱的紋理模式這對某些特定方向性缺陷可能是壞事。基于這些分析我通常使用如下增強參數關閉hsv_h到0.015hsv_s維持0.5但飽和度的作用本來就小mosaic在最后10個epoch關閉mixup設置為0.1copy_paste開啟到0.3。這套參數在NEU-DET上比默認參數mAP高約1.5到2個百分點主要是mAP50-95的提升因為小缺陷的定位更穩定了。實際效果會隨數據分布變化但不建議直接使用默認增強配置。數據集整理完成后還需要在YOLOv8的配置YAML里寫入路徑和類別名。train.txt和val.txt可以使用絕對路徑列表也可以只寫圖片目錄路徑讓Ultralytics自動掃描。路徑建議使用絕對路徑而不是相對路徑因為Qt界面調用時工作目錄可能被切換。3. 訓練鋼材缺陷檢測權重的完整流程從C2f結構到損失函數曲線圖3.1 YOLOv8環境配置與訓練參數的確定YOLOv8的依賴環境搭建相對輕量核心是ultralytics這個PyPI包它會自動拉取PyTorch、OpenCV、Pandas等依賴。推薦使用conda創建獨立環境Python版本選3.9或3.10PyTorch版本根據CUDA版本選擇。# 創建環境并安裝依賴 conda create -n steel_yolov8 python3.10 -y conda activate steel_yolov8 pip install ultralytics8.2.0 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118這段命令里--index-url指定了CUDA 11.8版本的PyTorch輪子。如果顯卡驅動支持CUDA 12.1可以換成cu121。安裝完可以用python -c import torch; print(torch.cuda.is_available())檢查GPU是否可用。GTX1660Ti這種6GB顯存的卡也能訓練YOLOv8s但batch size只能設為8輸入分辨率調整到640。訓練時核心參數集中在模型尺寸、輸入分辨率、批量大小和學習率四塊。鋼材缺陷檢測場景缺陷尺寸小輸入分辨率建議從默認的640提升到800或960。提高分辨率會帶來顯存壓力比如GTX1660Ti上6GB顯存imgsz800時batch size只能開到4imgsz960時可能直接OOM。顯存不夠時優先降低batch而不是降低分辨率因為小缺陷的檢測效果對分辨率更敏感。學習率權重上YOLOv8默認使用AdamW優化器lr0默認0.0001加入weight_decay0.0005。對于NEU-DET這類小數據集用較小的lr0能避免前期震蕩我一般設0.0002。訓練200個epoch后如果驗證集loss還在下降可以續訓而不是從頭開始。3.2 model.train()訓練與損失函數曲線圖繪制使用Ultralytics訓練接口時數據集YAML文件是唯一的配置入口。假設鋼材缺陷數據集目錄結構為steel_data/下面有images/train、images/val、labels/train、labels/val那么YAML可以寫成這樣# steel_dataset.yaml path: /home/user/steel_data train: images/train val: images/val nc: 6 names: 0: crazing 1: inclusion 2: patches 3: pitted_surface 4: rolled-in_scale 5: scratches啟動訓練的命令可以放在腳本文件里方便記錄實驗參數# train_steel.py from ultralytics import YOLO model YOLO(yolov8s.pt) results model.train( datasteel_dataset.yaml, epochs200, imgsz800, batch4, optimizerAdamW, lr00.0002, weight_decay0.0005, hsv_h0.015, hsv_s0.5, hsv_v0.4, mosaic0.8, mixup0.1, copy_paste0.3, device0, projectsteel_run, nameexp1 )每個訓練參數都有明確的理由。imgsz800是因為鋼材缺陷里的小目標在640分辨率下可能只占幾個像素batch4受限于6GB顯存mosaic0.8而不是1.0給最后10個epoch留出關閉余量Ultralytics內部會在最后10個epoch自動降低mosaic到0但保險起見顯式設置copy_paste0.3對圓形或塊狀缺陷如麻點、夾雜有效對長條狀劃痕意義不大。訓練完成后在steel_run/exp1/weights/下會生成best.pt和last.pt兩個權重文件。best.pt依據驗證集mAP挑選last.pt是最后一個epoch的狀態。如果顯存充足且訓練穩定best.pt基本能代表最優權重如果訓練后期過擬合last.pt可能反而比best.pt泛化更好建議兩個都跑一遍測試集對比。損失函數曲線圖是判斷訓練是否健康的第一手資料。YOLOv8的results.csv里記錄了train/box_loss、train/cls_loss、train/dfl_loss和對應的驗證集損失。用Matplotlib畫出來主要看三點第一box_loss是否平滑下降有震蕩說明學習率太高第二cls_loss和val/cls_loss之間的縫隙是否變大變大說明過擬合第三dfl_loss在最后50個epoch如果還在下降說明分布焦點損失還在起作用可以加訓。import pandas as pd import matplotlib.pyplot as plt # 讀取訓練日志 df pd.read_csv(steel_run/exp1/results.csv) fig, axes plt.subplots(1, 3, figsize(15, 4)) # 邊框損失 axes[0].plot(df[epoch], df[train/box_loss], labeltrain_box) axes[0].plot(df[epoch], df[val/box_loss], labelval_box) axes[0].set_title(Box Loss) axes[0].legend() # 分類損失 axes[1].plot(df[epoch], df[train/cls_loss], labeltrain_cls) axes[1].plot(df[epoch], df[val/cls_loss], labelval_cls) axes[1].set_title(Cls Loss) axes[1].legend() # 分布焦點損失 axes[2].plot(df[epoch], df[train/dfl_loss], labeltrain_dfl) axes[2].plot(df[epoch], df[val/dfl_loss], labelval_dfl) axes[2].set_title(DFL Loss) axes[2].legend() plt.tight_layout() plt.savefig(steel_loss_curve.png, dpi150)這段代碼把三組損失曲線畫在同一張圖里。畫出來后如果發現val/box_loss在100個epoch后不再下降說明模型容量或數據增強達到瓶頸繼續訓練只會過擬合分類分支。使用GTX1660Ti跑YOLOv8s在imgsz800下每個epoch大約需要2到3分鐘200個epoch約7到10小時這個時間成本可以接受。如果時間緊張可以訓練YOLOv8n模型參數量降低一半以上mAP大約下降3到4個點但推理速度從20毫秒降到7毫秒。3.3 缺陷檢測權重的評估標準與過擬合判斷訓練完成后不要只看results.csv里的mAP要跑一遍測試集逐類看各類缺陷的AP值。YOLOv8在訓練過程中會輸出驗證集混淆矩陣可以用predict方法在獨立的測試目錄上做推理然后對比標注文件計算每個類別的精確率和召回率。from ultralytics import YOLO model YOLO(steel_run/exp1/weights/best.pt) # 在驗證集上跑評估 metrics model.val( datasteel_dataset.yaml, splitval, imgsz800, conf0.25, iou0.6 ) print(metrics.box.map) # mAP50-95 print(metrics.box.map50) # mAP50 # 逐類查看AP print(metrics.box.ap_class_index) print(metrics.box.ap)鋼材缺陷檢測里最常見的現象是crazing裂紋這類缺陷的AP普遍偏低。裂紋形態不規則、對比度低、有時只出現在小范圍紋理異常區域即使人工標注也有較大主觀性。如果某類AP特別低優先檢查的是標注質量看看這類缺陷的標注框是否有漏標在600張訓練集里漏標5個框就可能讓AP下降好幾個點。其次是數據增強hsv_h如果過高會讓裂紋的灰度變化被色相干擾我在3.1里的參數就是針對這個問題調的。4. Qt界面GUI實現的完整方案PyQt5封裝YOLOv8推理全流程4.1 推理引擎封裝與并行推理避免界面卡頓Qt界面這部分的目標很明確讓現場人員雙擊啟動程序選擇圖片或視頻點擊按鈕就能看到檢測結果和置信度標注。技術棧選擇PyQt5加Ultralytics自帶推理接口不額外引入ONNX Runtime或TensorRT的復雜度。但有一個核心問題必須先解決YOLOv8推理和UI必須分開線程執行否則推理期間界面會卡死。PyQt5的QThread方式實現這一步最直接。Worker類繼承QThread重寫run()方法執行推理通過信號把檢測結果的圖片和統計數據傳回主線程。主線程只負責更新QLabel上的畫面。import sys import cv2 import numpy as np from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QMainWindow, QLabel, QPushButton, QFileDialog, QVBoxLayout, QWidget, QProgressBar, QComboBox from ultralytics import YOLO class DetectWorker(QThread): # 定義信號檢測完成的圖片、檢測到的目標數量和類別分布 result_ready pyqtSignal(object, int, dict) error_occurred pyqtSignal(str) def __init__(self, model_path, source_path, conf_thres0.25, iou_thres0.6, parentNone): super().__init__(parent) self.model YOLO(model_path) self.source_path source_path self.conf_thres conf_thres self.iou_thres iou_thres def run(self): try: # 使用stream模式處理視頻避免一次性加載所有幀導致內存不足 results self.model.predict( sourceself.source_path, confself.conf_thres, iouself.iou_thres, imgsz800, streamTrue, device0 ) for i, r in enumerate(results): im r.plot() # 繪制了檢測框的結果圖 class_counts {} boxes r.boxes if boxes is not None: cls_ids boxes.cls.cpu().numpy().astype(int) for cid in cls_ids: class_name r.names[cid] class_counts[class_name] class_counts.get(class_name, 0) 1 self.result_ready.emit(im, i, class_counts) except Exception as e: self.error_occurred.emit(str(e))Worker構造時傳入model_path和source_pathrun()內部使用model.predict的streamTrue參數這樣返回的是生成器每處理完一幀就發一次信號。r.plot()返回的是BGR格式的numpy數組這個數組可以直接轉成Qt的QImage顯示。device0指定用GPU推理注意這里device參數和訓練時完全一致。Qt主窗口負責接收信號并更新界面。一個關鍵的處理是圖像縮放相機圖片可能是4000x3000分辨率直接塞進QLabel會超出界面范圍需要按比例縮放到QLabel的可用尺寸同時保持長寬比。這里要用Qt.KeepAspectRatio的縮放模式并且轉換顏色通道時要指定QImage.Format_BGR888因為OpenCV讀出來的是BGR格式直接用Format_RGB888會讓畫面偏藍。class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(鋼材表面缺陷檢測系統 - YOLOv8) self.setMinimumSize(1200, 800) self.worker None # UI布局 central QWidget(self) layout QVBoxLayout(central) self.image_label QLabel(請選擇圖片或視頻開始檢測) self.image_label.setAlignment(Qt.AlignCenter) self.image_label.setStyleSheet(background: #2b2b2b; color: #fff;) self.image_label.setMinimumHeight(600) layout.addWidget(self.image_label) btn_row QVBoxLayout() self.btn_open_image QPushButton(選擇圖片) self.btn_open_video QPushButton(選擇視頻) self.btn_open_image.clicked.connect(self.open_image) self.btn_open_video.clicked.connect(self.open_video) btn_row.addWidget(self.btn_open_image) btn_row.addWidget(self.btn_open_video) layout.addLayout(btn_row) self.status_label QLabel(就緒) layout.addWidget(self.status_label) self.setCentralWidget(central) def open_image(self): fname, _ QFileDialog.getOpenFileName( self, 選擇圖片, , 圖片文件 (*.jpg *.jpeg *.png *.bmp)) if not fname: return self.status_label.setText(f正在檢測: {fname}) self.worker DetectWorker(steel_run/exp1/weights/best.pt, fname) self.worker.result_ready.connect(self.update_image) self.worker.error_occurred.connect(self.handle_error) self.worker.start() def update_image(self, img_array, frame_idx, class_counts): h, w, ch img_array.shape qimg QImage(img_array.data, w, h, ch * w, QImage.Format_BGR888) pixmap QPixmap.fromImage(qimg) # 按QLabel大小縮放保持長寬比 scaled pixmap.scaled( self.image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation) self.image_label.setPixmap(scaled) total sum(class_counts.values()) detail , .join([f{k}: {v} for k, v in class_counts.items()]) self.status_label.setText(f檢測到 {total} 個缺陷 | {detail})update_image先根據圖像數組創建QImage再構造QPixmap最后按label尺寸縮放。注意這里必須把img_array.data引用傳給QImage后不能立刻釋放img_array否則Qt訪問到懸空內存會崩潰。縮放使用SmoothTransformation模式雖然慢一點但表面缺陷的小目標能保持清晰度。為什么設置ch * w作為bytesPerLine因為OpenCV的numpy數組默認按行連續存儲當圖像寬度不是4的倍數時這個參數決定了Qt按行讀取的步長錯誤設置會導致圖像出現錯位條紋。4.2 Qt界面GUI操作流程與置信度參數調節視頻檢測時QThread信號是按幀觸發的每一幀都會更新一次界面。但如果視頻幀率高于UI刷新率信號隊列會積壓界面越來越卡。解決方案是設置QThread.setPriority(QThread.HighPriority)降低線程優先級同時容忍界面跳幀顯示。另一個可行方案是只更新最新的信號在update_image里用一個self.latest_result img_array保存最新幀再用QTimer每100毫秒刷新一次減少QPixmap構造次數。置信度閾值conf_thres應該做成界面上的實時可調參數而不是寫死在構造函數里。鋼材缺陷檢測的典型誤報來源有兩類一類是表面水滴造成的反光被識別為patches另一類是氧化鐵皮邊緣被識別為inclusion。前者通過提高置信度到0.3到0.35能濾除大部分后者需要結合現場經驗判斷不建議把置信度調得太高否則真實的小缺陷也會被濾掉。def update_conf_threshold(self, value): # value是滑動條的數值0到100映射到0到1的置信度 self.conf_thres value / 100.0 if hasattr(self, worker) and self.worker is not None: self.worker.conf_thres self.conf_thres滑動條聯動到worker的conf_thres屬性這樣在視頻推理過程中也能實時調整不用重啟程序。這個設計在產線試用階段很實用現場人員會根據自己的判斷不斷微調閾值直到找到誤報和漏報的平衡點。Qt界面里另一個容易被忽略的點是中文路徑。現場質檢人員把圖片文件命名為缺陷樣本20250315_劃痕.jpg是非常常見的情況。Ultralytics的predict接口對包含中文的路徑處理依賴底層文件系統編碼Windows上容易出現UnicodeDecodeError。穩妥做法是在選擇文件后先復制到程序目錄下的cache文件夾用ASCII文件名保存再傳入推理接口。4.3 權重文件打包與界面發布離線環境怎么部署程序調試完成后需要打包成獨立的exe給現場用。PyInstaller是PyQt5項目最成熟的打包工具。但含YOLOv8的打包有幾個坑Ultralytics在運行時會動態加載一些配置文件和字體文件如果直接打包主腳本運行時可能找不到Arial.ttf導致繪圖文字變成方塊模型權重文件.pt雖然會被Python代碼引用但PyInstaller不會自動收集非代碼資源。# 打包命令注意需要顯式添加ultralytics的配置文件和字體 pyinstaller -F -w \ --name SteelDetect \ --add-data steel_run/exp1/weights/best.pt;weights \ --collect-all ultralytics \ --collect-all torch \ app.py--collect-all ultralytics收集所有包內資源--collect-all torch是必須的因為PyTorch有大量動態庫依賴。-F生成單文件模式啟動時會自解壓到臨時目錄首次啟動可能有3到5秒延遲。如果閃爍窗口會引起現場人員困惑可以先用-w隱藏控制臺再在打包命令里加上--icon指定應用圖標。--add-data在Windows下用分號分隔源和目標目錄。打包后單文件可能達到1.5GB以上因為PyTorch的CUDA運行時全量打進去了。如果想瘦身可以在打包前把Ultralytics的依賴精簡去掉不用的模型配置但保險起見不建議新手優化這點體積。磁盤空間充裕就保持原樣。5. 三個落地技巧批量推理、缺陷分類統計與界面熱更新5.1 批量推理文件夾圖片并導出Excel統計表產線質檢場景往往不是單張圖片檢測而是每隔幾秒生成一張截圖一個班次下來幾百張圖片需要批量處理。用Qt界面逐張點擊不現實常見做法是程序啟動時自動掃描指定目錄下的所有圖片批量推理后把結果匯總成一份CSV或Excel。import os import glob import pandas as pd from ultralytics import YOLO model YOLO(steel_run/exp1/weights/best.pt) def batch_inference_and_export(image_dir, output_csv): image_files glob.glob(os.path.join(image_dir, *.jpg)) \ glob.glob(os.path.join(image_dir, *.png)) results [] for img_path in image_files: r model.predict(sourceimg_path, conf0.25, iou0.6, imgsz800, verboseFalse)[0] # 統計每個類別的數量和最大置信度 class_counts {} confidences {} for box in r.boxes: cls_id int(box.cls[0].cpu().numpy()) cls_name r.names[cls_id] conf float(box.conf[0].cpu().numpy()) class_counts[cls_name] class_counts.get(cls_name, 0) 1 if cls_name not in confidences or conf confidences[cls_name]: confidences[cls_name] conf row {圖片路徑: img_path, 總缺陷數: sum(class_counts.values())} for name in r.names.values(): row[f數量_{name}] class_counts.get(name, 0) row[f最大置信度_{name}] round(confidences.get(name, 0), 4) results.append(row) df pd.DataFrame(results) df.to_csv(output_csv, indexFalse, encodingutf-8-sig) print(f已導出 {len(results)} 條檢測記錄到 {output_csv})這條批量推理的核心價值在于輸出格式直接貼合質檢追溯需求。utf-8-sig編碼可以在Excel里正常顯示中文路徑不需要額外轉碼。導出每一類的最大置信度有什么意義最大置信度代表這張圖片里最可能是該類缺陷的區域質檢人員可以按最大置信度降序排列優先查看最存疑的樣本。5.2 Qt界面顯示檢測結果時的類別顏色映射與實時刷新優化YOLOv8的r.plot()繪圖函數默認給每個類別分配一種顏色但相鄰類別的顏色可能相近。鋼材缺陷里inclusion和pitted_surface經常出現在同一區域顏色相近時現場人員容易看混。更好的做法是手動指定每個類別的確切RGB值。CLASS_COLORS { crazing: (0, 0, 255), # 紅色 inclusion: (0, 255, 0), # 綠色 patches: (255, 0, 0), # 藍色 pitted_surface: (0, 255, 255), # 黃色 rolled-in_scale: (255, 0, 255), # 品紅 scratches: (255, 255, 0), # 青色 } def draw_with_custom_colors(img, results, class_names): 在圖像上繪制帶固定顏色和中文標簽的檢測框 import cv2 as cv font cv.FONT_HERSHEY_SIMPLEX for box in results.boxes: cls_id int(box.cls[0].cpu().numpy()) conf float(box.conf[0].cpu().numpy()) x1, y1, x2, y2 box.xyxy[0].cpu().numpy().astype(int) color CLASS_COLORS.get(class_names[cls_id], (255, 255, 255)) cv.rectangle(img, (x1, y1), (x2, y2), color, 2) label f{class_names[cls_id]} {conf:.2f} # 文字背景色墊底避免被鋼材紋理干擾 (tw, th), baseline cv.getTextSize(label, font, 0.6, 1) cv.rectangle(img, (x1, y1-th-baseline), (x1tw, y1), color, -1) cv.putText(img, label, (x1, y1-baseline), font, 0.6, (255,255,255), 1) return img除了自定義顏色外這里額外做了一個操作在文字背后畫實心矩形墊底。鋼材表面紋理復雜直接繪制文字會和背景噪聲重疊可讀性差。實心背景代價是幾個像素的高不犧牲推理速度。如果想要中文標簽寫入圖片需要下載中文字體并使用cv.putText的freetype版本這會增加依賴建議界面語言用中文但圖片繪制文字保持英文避免現場人員調試時困惑。5.3 驗證最終權重效果的四步檢查法與常見問題速查部署到現場前用一套固定步驟驗證權重質量非常關鍵。我建議按下面四步做第一步用訓練時用的驗證集跑一遍model.val()記錄mAP50和mAP50-95。第二步選擇10張典型缺陷圖片包括每類缺陷各2張單張跑推理并人工核對所有標注框是否正確對應到缺陷區域。第三步選擇5張無缺陷的正常鋼材圖片確認沒有誤報。第四步用一段產線實際視頻連續跑5分鐘統計每幀推理耗時是否穩定在30毫秒以內。# 用YOLOv8自帶命令行快速驗證單張圖片 yolo detect predict modelsteel_run/exp1/weights/best.pt source./test_samples/crazing_001.jpg imgsz800 conf0.25 iou0.6 save如果發現檢測結果不理想先檢查三個地方。第一訓練時的imgsz和推理時的imgsz必須一致推理時把分辨率從640改成800模型在640下學到的小目標特征在800分辨率下計算感受野時會錯位。第二檢查是否誤把last.pt當成best.pt加載兩個文件在大小和性能上有明顯差異。第三檢查推理時是否忘記設置device0CPU推理速度和GPU能差3到5倍。如果現場反饋某類缺陷總是漏檢優先查的是訓練集里該類缺陷的標注是否有遺漏。鋼材缺陷檢測任務中標注質量的影響遠大于模型結構的影響一個漏標可能導致模型把正常區域預測為背景。用第5.1節的批量推理工具跑一遍訓練集把預測置信度大于0.5但標注文件中沒有記錄的地方標注出來人工確認是漏標還是誤檢。這個方法能快速發現數據問題比反復調模型參數更有效。本文還有配套的精品資源點擊獲取