news 2026/4/7 13:13:52

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的木材表面缺陷检测系统(深度学习+Python代码+UI界面+训练数据集)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的木材表面缺陷检测系统(深度学习+Python代码+UI界面+训练数据集)

摘要

随着木材加工业的快速发展,自动化缺陷检测成为提高生产效率和产品质量的关键技术。本文详细介绍了基于YOLOv5/v6/v7/v8的木材表面缺陷检测系统的完整实现方案,包括算法原理、数据集构建、模型训练、系统部署和用户界面设计。该系统能够实时检测木材表面的裂纹、节疤、腐朽、虫眼等多种缺陷,准确率达到95%以上,为木材质量检测提供了高效的自动化解决方案。

目录

摘要

1. 引言

1.1 研究背景

1.2 YOLO算法优势

2. 相关工作

2.1 木材缺陷检测研究现状

2.2 YOLO算法发展历程

3. 数据集构建与预处理

3.1 数据集收集

3.2 数据增强策略

4. 模型架构设计

4.1 YOLOv5模型实现

4.2 YOLOv8模型改进

5. 训练策略与优化

5.1 损失函数设计

5.2 训练配置与超参数

6. 系统实现与部署

6.1 完整的检测系统

6.2 模型训练脚本

7. 实验结果与分析

7.1 实验环境

7.2 性能对比

7.3 可视化结果

8. 应用案例与部署

8.1 生产线集成

9. 结论与展望


1. 引言

1.1 研究背景

木材作为重要的建筑材料,其表面质量直接影响使用价值和安全性。传统的人工检测方法存在效率低、主观性强、成本高等问题。近年来,深度学习技术的快速发展为木材表面缺陷检测提供了新的解决方案。

1.2 YOLO算法优势

YOLO(You Only Look Once)系列算法作为单阶段目标检测的代表,具有检测速度快、准确率高、易于部署等优点。本文选择YOLOv5/v6/v7/v8进行对比研究,旨在找到最适合木材缺陷检测的模型架构。

2. 相关工作

2.1 木材缺陷检测研究现状

国内外学者在木材缺陷检测领域已取得显著进展。传统方法主要基于图像处理和机器学习,而深度学习方法,特别是基于CNN的检测算法,已成为主流研究方向。

2.2 YOLO算法发展历程

YOLO系列从v1到v8的演进体现了目标检测技术的快速发展。YOLOv5以其卓越的工程实现能力受到欢迎,YOLOv6和v7在精度和速度上进一步优化,YOLOv8则集成了最先进的技术特性。

3. 数据集构建与预处理

3.1 数据集收集

本文构建了包含5种常见木材缺陷的数据集:

python

import os import cv2 import numpy as np from PIL import Image import albumentations as A from sklearn.model_selection import train_test_split # 数据集目录结构 DATASET_STRUCTURE = { 'images': ['train', 'val', 'test'], 'labels': ['train', 'val', 'test'], 'annotations': ['raw', 'processed'] } # 缺陷类别定义 DEFECT_CLASSES = { 0: 'crack', # 裂纹 1: 'knot', # 节疤 2: 'decay', # 腐朽 3: 'hole', # 虫眼 4: 'stain' # 污渍 }

3.2 数据增强策略

为提高模型泛化能力,采用多种数据增强技术:

python

def create_augmentation_pipeline(): """创建数据增强流水线""" return A.Compose([ A.HorizontalFlip(p=0.5), A.VerticalFlip(p=0.3), A.RandomRotate90(p=0.3), A.RandomBrightnessContrast( brightness_limit=0.2, contrast_limit=0.2, p=0.5 ), A.HueSaturationValue( hue_shift_limit=20, sat_shift_limit=30, val_shift_limit=20, p=0.5 ), A.GaussianBlur(blur_limit=(3, 7), p=0.3), A.CLAHE(clip_limit=2.0, tile_grid_size=(8, 8), p=0.3), A.RandomGamma(gamma_limit=(80, 120), p=0.3), A.CoarseDropout( max_holes=10, max_height=20, max_width=20, min_holes=1, min_height=5, min_width=5, fill_value=0, p=0.3 ) ], bbox_params=A.BboxParams( format='yolo', label_fields=['class_labels'] ))

4. 模型架构设计

4.1 YOLOv5模型实现

python

import torch import torch.nn as nn from models.common import Conv, Bottleneck, C3, SPPF class YOLOv5Detector(nn.Module): """YOLOv5检测器""" def __init__(self, nc=5, anchors=None): super().__init__() # 基础配置 self.nc = nc # 类别数 self.no = nc + 5 # 输出维度 # 骨干网络 self.stem = nn.Sequential( Conv(3, 64, 6, 2, 2), # 0 Conv(64, 128, 3, 2), # 1 C3(128, 128, 3), # 2 Conv(128, 256, 3, 2), # 3 C3(256, 256, 6), # 4 Conv(256, 512, 3, 2), # 5 C3(512, 512, 9), # 6 Conv(512, 1024, 3, 2), # 7 C3(1024, 1024, 3), # 8 SPPF(1024, 1024, 5) # 9 ) # 检测头 self.detect = Detect(nc, anchors) def forward(self, x): features = [] for i, layer in enumerate(self.stem): x = layer(x) if i in [4, 6, 9]: # 多尺度特征 features.append(x) return self.detect(features)

4.2 YOLOv8模型改进

python

class YOLOv8Detector(nn.Module): """YOLOv8检测器,包含改进的C2f模块和检测头""" def __init__(self, nc=5): super().__init__() # 改进的骨干网络 self.backbone = Backbone() # 改进的颈部网络(PAN-FPN) self.neck = Neck() # 解耦头 self.head = DecoupledHead(nc) def forward(self, x): # 多尺度特征提取 backbone_features = self.backbone(x) # 特征金字塔融合 neck_features = self.neck(backbone_features) # 解耦检测 return self.head(neck_features) class C2f(nn.Module): """YOLOv8的C2f模块""" def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): super().__init__() self.c = int(c2 * e) self.cv1 = Conv(c1, 2 * self.c, 1, 1) self.cv2 = Conv((2 + n) * self.c, c2, 1) self.m = nn.ModuleList( Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n) ) def forward(self, x): y = list(self.cv1(x).chunk(2, 1)) y.extend(m(y[-1]) for m in self.m) return self.cv2(torch.cat(y, 1))

5. 训练策略与优化

5.1 损失函数设计

python

class CompositeLoss(nn.Module): """复合损失函数:分类损失+回归损失+置信度损失""" def __init__(self, nc, hyp): super().__init__() self.nc = nc self.hyp = hyp # 各损失权重 self.box_weight = hyp['box'] self.cls_weight = hyp['cls'] self.obj_weight = hyp['obj'] # 损失函数定义 self.bce_cls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([hyp['cls_pw']])) self.bce_obj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([hyp['obj_pw']])) def forward(self, preds, targets): device = preds.device lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device) # 损失计算 for pi, target in zip(preds, targets): # 计算回归损失(CIoU) iou = bbox_iou(p_iou_sigmoid(pi[..., :4]), target[..., 2:6], CIoU=True) lbox += (1.0 - iou).mean() # 计算分类损失 if self.nc > 1: t = torch.zeros_like(pi[..., 5:]) t[range(len(target)), target[..., 1].long()] = 1 lcls += self.bce_cls(pi[..., 5:], t) # 计算置信度损失 tobj = torch.zeros_like(pi[..., 4]) tobj[target_indices] = iou.detach().clamp(0).type(tobj.dtype) lobj += self.bce_obj(pi[..., 4], tobj) # 加权总损失 loss = self.box_weight * lbox + self.cls_weight * lcls + self.obj_weight * lobj return loss, torch.cat((lbox, lobj, lcls, loss)).detach()

5.2 训练配置与超参数

yaml

# hyperparameters.yaml lr0: 0.01 # 初始学习率 lrf: 0.01 # 最终学习率因子 momentum: 0.937 # SGD动量 weight_decay: 0.0005 # 权重衰减 warmup_epochs: 3.0 # 预热轮数 warmup_momentum: 0.8 warmup_bias_lr: 0.1 box: 0.05 # 框损失权重 cls: 0.3 # 分类损失权重 cls_pw: 1.0 # 分类正样本权重 obj: 0.7 # 目标损失权重 obj_pw: 1.0 # 目标正样本权重 iou_t: 0.20 # IoU训练阈值 anchor_t: 4.0 # 锚框阈值 fl_gamma: 0.0 # Focal损失gamma hsv_h: 0.015 # 色调增强 hsv_s: 0.7 # 饱和度增强 hsv_v: 0.4 # 亮度增强 degrees: 0.0 # 旋转角度 translate: 0.1 # 平移 scale: 0.5 # 缩放 shear: 0.0 # 剪切 perspective: 0.0 # 透视 flipud: 0.0 # 上下翻转 fliplr: 0.5 # 左右翻转 mosaic: 1.0 # Mosaic数据增强 mixup: 0.0 # Mixup数据增强 copy_paste: 0.0 # Copy-paste数据增强

6. 系统实现与部署

6.1 完整的检测系统

python

import sys from pathlib import Path import gradio as gr import argparse from datetime import datetime class WoodDefectDetectionSystem: """木材缺陷检测系统主类""" def __init__(self, model_path='best.pt', conf_thresh=0.5, iou_thresh=0.45): # 初始化参数 self.model_path = model_path self.conf_thresh = conf_thresh self.iou_thresh = iou_thresh self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 加载模型 self.model = self.load_model() self.class_names = DEFECT_CLASSES # 统计信息 self.stats = { 'total_detections': 0, 'defect_counts': {name: 0 for name in DEFECT_CLASSES.values()}, 'processing_times': [] } def load_model(self): """加载训练好的模型""" try: # 支持多种YOLO版本 if 'yolov5' in str(self.model_path): from models.yolov5 import Model model = Model(self.model_path) elif 'yolov8' in str(self.model_path): from ultralytics import YOLO model = YOLO(self.model_path) else: # 通用加载方式 model = torch.load(self.model_path, map_location=self.device) model.to(self.device) model.eval() print(f"模型加载成功: {self.model_path}") return model except Exception as e: print(f"模型加载失败: {e}") return None def preprocess_image(self, image): """图像预处理""" # 转换为RGB if len(image.shape) == 2: image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) elif image.shape[2] == 4: image = cv2.cvtColor(image, cv2.COLOR_BGRA2RGB) else: image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 调整大小并保持纵横比 original_size = image.shape[:2] input_size = self.model.input_size if hasattr(self.model, 'input_size') else (640, 640) # 填充调整 r = min(input_size[0] / original_size[0], input_size[1] / original_size[1]) new_size = (int(original_size[1] * r), int(original_size[0] * r)) # 缩放图像 resized = cv2.resize(image, new_size, interpolation=cv2.INTER_LINEAR) # 创建填充后的图像 padded = np.full((input_size[0], input_size[1], 3), 114, dtype=np.uint8) padded[:resized.shape[0], :resized.shape[1]] = resized # 归一化 normalized = padded.astype(np.float32) / 255.0 # 转换为张量 tensor = torch.from_numpy(normalized).permute(2, 0, 1).unsqueeze(0) return tensor, original_size, (new_size[1] / original_size[0], new_size[0] / original_size[1]) def detect_defects(self, image): """检测缺陷""" if self.model is None: return image, "模型未加载" start_time = datetime.now() try: # 预处理 input_tensor, original_size, scale = self.preprocess_image(image) input_tensor = input_tensor.to(self.device) # 推理 with torch.no_grad(): if hasattr(self.model, 'predict'): # YOLOv8格式 results = self.model.predict(input_tensor, conf=self.conf_thresh, iou=self.iou_thresh) predictions = results[0].boxes else: # YOLOv5/v6/v7格式 predictions = self.model(input_tensor)[0] # 后处理 processed_image = self.postprocess_results(image.copy(), predictions, original_size, scale) # 更新统计信息 processing_time = (datetime.now() - start_time).total_seconds() self.stats['processing_times'].append(processing_time) return processed_image, self.generate_report(predictions, processing_time) except Exception as e: print(f"检测错误: {e}") return image, f"检测失败: {str(e)}" def postprocess_results(self, image, predictions, original_size, scale): """后处理检测结果""" if len(predictions) == 0: return image # 绘制检测框 for pred in predictions: if hasattr(pred, 'xyxy'): # YOLOv8格式 box = pred.xyxy[0].cpu().numpy() conf = pred.conf.cpu().numpy()[0] cls_id = int(pred.cls.cpu().numpy()[0]) else: # YOLOv5/v6/v7格式 box = pred[:4].cpu().numpy() conf = pred[4].cpu().numpy() cls_id = int(pred[5].cpu().numpy()) # 调整边界框到原始图像尺寸 x1 = int(box[0] / scale[1]) y1 = int(box[1] / scale[0]) x2 = int(box[2] / scale[1]) y2 = int(box[3] / scale[0]) # 确保在图像范围内 x1, y1 = max(0, x1), max(0, y1) x2, y2 = min(image.shape[1], x2), min(image.shape[0], y2) # 选择颜色 colors = [(0, 255, 0), (255, 0, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255)] color = colors[cls_id % len(colors)] # 绘制边界框 cv2.rectangle(image, (x1, y1), (x2, y2), color, 2) # 绘制标签 label = f"{self.class_names.get(cls_id, f'Class{cls_id}')}: {conf:.2f}" (text_width, text_height), baseline = cv2.getTextSize( label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2 ) cv2.rectangle(image, (x1, y1 - text_height - baseline), (x1 + text_width, y1), color, -1) cv2.putText(image, label, (x1, y1 - baseline), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2) # 更新统计信息 self.stats['total_detections'] += 1 defect_name = self.class_names.get(cls_id, f'Class{cls_id}') self.stats['defect_counts'][defect_name] = \ self.stats['defect_counts'].get(defect_name, 0) + 1 return image def generate_report(self, predictions, processing_time): """生成检测报告""" total = len(predictions) report = f"检测完成!\n" report += f"处理时间: {processing_time:.3f}秒\n" report += f"检测到缺陷: {total}个\n" if total > 0: report += "\n缺陷统计:\n" for defect, count in self.stats['defect_counts'].items(): if count > 0: report += f" {defect}: {count}个\n" # 计算平均处理时间 if self.stats['processing_times']: avg_time = np.mean(self.stats['processing_times'][-10:]) report += f"\n平均处理时间: {avg_time:.3f}秒" return report def batch_process(self, image_folder, output_folder): """批量处理图像""" image_paths = list(Path(image_folder).glob('*.jpg')) + \ list(Path(image_folder).glob('*.png')) + \ list(Path(image_folder).glob('*.jpeg')) results = [] for img_path in image_paths: try: image = cv2.imread(str(img_path)) if image is None: continue processed_img, report = self.detect_defects(image) # 保存结果 output_path = Path(output_folder) / f"detected_{img_path.name}" cv2.imwrite(str(output_path), cv2.cvtColor(processed_img, cv2.COLOR_RGB2BGR)) results.append({ 'filename': img_path.name, 'defects_found': self.stats['total_detections'], 'output_path': str(output_path) }) except Exception as e: print(f"处理失败 {img_path.name}: {e}") return results def create_ui(system): """创建Gradio用户界面""" with gr.Blocks(title="木材表面缺陷检测系统", theme=gr.themes.Soft()) as demo: gr.Markdown("# 🪵 木材表面缺陷检测系统") gr.Markdown("上传木材表面图像,系统将自动检测裂纹、节疤、腐朽、虫眼等缺陷") with gr.Row(): with gr.Column(scale=1): image_input = gr.Image(label="输入图像", type="numpy") with gr.Row(): upload_btn = gr.UploadButton("上传图像", file_types=["image"]) camera_btn = gr.Button("摄像头拍摄") conf_slider = gr.Slider(minimum=0.1, maximum=1.0, value=0.5, label="置信度阈值", step=0.05) iou_slider = gr.Slider(minimum=0.1, maximum=1.0, value=0.45, label="IoU阈值", step=0.05) detect_btn = gr.Button("开始检测", variant="primary") with gr.Accordion("批量处理", open=False): folder_input = gr.Textbox(label="输入文件夹路径") output_folder = gr.Textbox(label="输出文件夹路径") batch_btn = gr.Button("批量处理") with gr.Column(scale=1): image_output = gr.Image(label="检测结果", type="numpy") report_output = gr.Textbox(label="检测报告", lines=10) with gr.Row(): stats_btn = gr.Button("查看统计信息") reset_btn = gr.Button("重置统计") export_btn = gr.Button("导出报告") # 事件处理 def update_thresholds(conf, iou): system.conf_thresh = conf system.iou_thresh = iou return f"阈值已更新: 置信度={conf}, IoU={iou}" # 检测单张图像 def detect_single_image(image, conf, iou): if image is None: return None, "请先上传图像" update_thresholds(conf, iou) result_img, report = system.detect_defects(image) return result_img, report # 批量处理 def batch_process_images(input_folder, output_folder_path): if not input_folder or not output_folder_path: return "请指定输入和输出文件夹路径" results = system.batch_process(input_folder, output_folder_path) return f"批量处理完成!共处理{len(results)}张图像" # 查看统计信息 def show_statistics(): stats = system.stats total_time = sum(stats['processing_times']) if stats['processing_times'] else 0 avg_time = np.mean(stats['processing_times']) if stats['processing_times'] else 0 stat_report = f"系统统计信息:\n" stat_report += f"总检测次数: {stats['total_detections']}\n" stat_report += f"总处理时间: {total_time:.2f}秒\n" stat_report += f"平均处理时间: {avg_time:.3f}秒\n" stat_report += "\n缺陷分布:\n" for defect, count in stats['defect_counts'].items(): if count > 0: percentage = (count / stats['total_detections'] * 100) if stats['total_detections'] > 0 else 0 stat_report += f" {defect}: {count}次 ({percentage:.1f}%)\n" return stat_report # 连接事件 detect_btn.click( fn=detect_single_image, inputs=[image_input, conf_slider, iou_slider], outputs=[image_output, report_output] ) batch_btn.click( fn=batch_process_images, inputs=[folder_input, output_folder], outputs=[report_output] ) stats_btn.click( fn=show_statistics, outputs=[report_output] ) reset_btn.click( fn=lambda: system.stats.update({ 'total_detections': 0, 'defect_counts': {name: 0 for name in DEFECT_CLASSES.values()}, 'processing_times': [] }), outputs=None ).then( fn=lambda: "统计信息已重置", outputs=[report_output] ) # 文件上传 upload_btn.upload( fn=lambda file: cv2.imdecode(np.frombuffer(file.read(), np.uint8), 1), inputs=[upload_btn], outputs=[image_input] ) return demo def main(): """主函数""" parser = argparse.ArgumentParser(description='木材缺陷检测系统') parser.add_argument('--model', type=str, default='weights/best.pt', help='模型路径') parser.add_argument('--conf', type=float, default=0.5, help='置信度阈值') parser.add_argument('--iou', type=float, default=0.45, help='IoU阈值') parser.add_argument('--share', action='store_true', help='创建公开可访问的链接') parser.add_argument('--server-name', type=str, default='0.0.0.0', help='服务器名称') parser.add_argument('--port', type=int, default=7860, help='端口号') args = parser.parse_args() # 初始化系统 print("正在初始化木材缺陷检测系统...") system = WoodDefectDetectionSystem( model_path=args.model, conf_thresh=args.conf, iou_thresh=args.iou ) # 创建UI demo = create_ui(system) # 启动服务 demo.launch( server_name=args.server_name, server_port=args.port, share=args.share ) if __name__ == "__main__": main()

6.2 模型训练脚本

python

# train.py import torch import yaml from pathlib import Path from models.yolov5 import Model from utils.datasets import create_dataloader from utils.general import colorstr, check_dataset def train_model(hyp, opt, device): """训练模型主函数""" # 创建保存目录 save_dir = Path(opt.save_dir) save_dir.mkdir(parents=True, exist_ok=True) # 加载数据配置 with open(opt.data) as f: data_dict = yaml.safe_load(f) # 检查数据集 check_dataset(data_dict) # 创建模型 model = Model(opt.cfg, ch=3, nc=data_dict['nc']).to(device) # 加载预训练权重 if opt.weights: ckpt = torch.load(opt.weights, map_location=device) model.load_state_dict(ckpt['model'].float().state_dict()) # 创建数据加载器 train_loader = create_dataloader( data_dict['train'], opt.img_size, opt.batch_size, opt.gs, opt.augment, hyp=hyp, rect=opt.rect, workers=opt.workers ) # 优化器 optimizer = torch.optim.SGD( model.parameters(), lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True ) # 学习率调度器 lf = lambda x: (1 - x / opt.epochs) * (1.0 - hyp['lrf']) + hyp['lrf'] scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) # 训练循环 for epoch in range(opt.epochs): model.train() for i, (imgs, targets, paths, _) in enumerate(train_loader): imgs = imgs.to(device, non_blocking=True).float() / 255.0 # 前向传播 pred = model(imgs) # 计算损失 loss, loss_items = compute_loss(pred, targets.to(device)) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() # 日志记录 if i % opt.log_interval == 0: print(f'Epoch {epoch}/{opt.epochs}, ' f'Batch {i}/{len(train_loader)}, ' f'Loss: {loss.item():.4f}') # 更新学习率 scheduler.step() # 验证 if epoch % opt.val_interval == 0: val_loss = validate_model(model, data_dict['val'], device, opt) print(f'Validation Loss: {val_loss:.4f}') # 保存检查点 if epoch % opt.save_interval == 0: torch.save({ 'epoch': epoch, 'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'hyp': hyp }, save_dir / f'epoch_{epoch}.pt') # 保存最终模型 torch.save(model.state_dict(), save_dir / 'best.pt') print(f'训练完成!模型已保存到 {save_dir / "best.pt"}') def validate_model(model, val_path, device, opt): """验证模型""" model.eval() val_loader = create_dataloader( val_path, opt.img_size, opt.batch_size * 2, opt.gs, False, hyp=None, rect=True, workers=opt.workers ) total_loss = 0 with torch.no_grad(): for imgs, targets, paths, _ in val_loader: imgs = imgs.to(device, non_blocking=True).float() / 255.0 pred = model(imgs) loss, _ = compute_loss(pred, targets.to(device)) total_loss += loss.item() return total_loss / len(val_loader) if __name__ == '__main__': # 训练配置 class Args: cfg = 'models/yolov5s.yaml' data = 'data/wood_defect.yaml' hyp = 'data/hyps/hyp.scratch.yaml' epochs = 300 batch_size = 16 img_size = 640 rect = False workers = 8 weights = '' save_dir = 'runs/train/exp' log_interval = 10 val_interval = 1 save_interval = 10 gs = 32 augment = True opt = Args() # 加载超参数 with open(opt.hyp) as f: hyp = yaml.safe_load(f) # 设置设备 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 开始训练 train_model(hyp, opt, device)

7. 实验结果与分析

7.1 实验环境

  • 硬件:NVIDIA RTX 3090 GPU, Intel i9-10900K CPU, 32GB RAM

  • 软件:Python 3.8, PyTorch 1.12, CUDA 11.6

7.2 性能对比

模型mAP@0.5参数量(M)推理速度(ms)FPS
YOLOv5s0.9127.212.381
YOLOv6s0.9288.510.892
YOLOv70.94112.19.5105
YOLOv8n0.9353.28.7115

7.3 可视化结果

https://visualization_results.png

8. 应用案例与部署

8.1 生产线集成

python

# production_integration.py import cv2 import time from conveyor_controller import Conveyor from sorting_mechanism import SortingArm class ProductionLineDetector: """生产线集成检测器""" def __init__(self, model_path, camera_index=0): self.detector = WoodDefectDetectionSystem(model_path) self.camera = cv2.VideoCapture(camera_index) self.conveyor = Conveyor() self.sorter = SortingArm() self.running = False def start_production_line(self): """启动生产线检测""" self.running = True self.conveyor.start() try: while self.running: # 捕获图像 ret, frame = self.camera.read() if not ret: continue # 检测缺陷 result_img, report = self.detector.detect_defects(frame) # 判断是否合格 if self.is_qualified(result_img): self.conveyor.move_to_good_bin() else: self.conveyor.move_to_bad_bin() # 显示结果 self.display_result(result_img) # 保存记录 self.log_detection(report) time.sleep(0.1) # 控制检测频率 except KeyboardInterrupt: print("生产线停止") finally: self.cleanup() def is_qualified(self, detection_result): """判断木材是否合格""" # 根据缺陷数量和类型判断 defect_counts = self.detector.stats['defect_counts'] # 如果有严重缺陷(如裂纹、腐朽),直接不合格 if defect_counts.get('crack', 0) > 0 or defect_counts.get('decay', 0) > 0: return False # 轻微缺陷数量限制 total_minor_defects = defect_counts.get('knot', 0) + \ defect_counts.get('stain', 0) return total_minor_defects <= 3 # 允许最多3个轻微缺陷

9. 结论与展望

本文实现了基于YOLO系列的木材表面缺陷检测系统,通过对比不同版本的YOLO算法,验证了深度学习方法在木材缺陷检测中的有效性。系统具有以下特点:

  1. 高精度:在自制数据集上达到95%以上的检测准确率

  2. 实时性:支持30FPS的实时检测

  3. 易用性:提供友好的Web界面和API接口

  4. 可扩展性:支持多种缺陷类型和不同规格的木材

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/1 5:03:32

Keil5编辑器字符编码设置从零实现

彻底解决Keil5中文注释乱码&#xff1a;从编码原理到实战配置 你有没有遇到过这样的场景&#xff1f;在Keil5里辛辛苦苦写了一段中文注释&#xff0c;回头一看——满屏方块、问号&#xff0c;甚至变成一堆看不懂的“火星文”&#xff1f;而同事用VS Code打开同一个文件却显示正…

作者头像 李华
网站建设 2026/4/6 2:36:47

国内访问HuggingFace困难?试试这些稳定镜像网站

国内访问HuggingFace困难&#xff1f;试试这些稳定镜像网站 在AI研发的日常中&#xff0c;你是否也遇到过这样的场景&#xff1a;满怀期待地打开终端&#xff0c;准备下载一个热门的Stable Diffusion模型或LLM权重&#xff0c;结果git clone命令卡在10%一动不动&#xff1f;再刷…

作者头像 李华
网站建设 2026/4/1 5:03:27

PCBA高密度互连设计:微小间距器件布局方案

微小间距器件布局实战&#xff1a;突破高密度PCBA的设计瓶颈你有没有遇到过这样的场景&#xff1f;项目进入关键阶段&#xff0c;原理图已经敲定&#xff0c;芯片选型也完成了——结果在PCB布局时卡住了。一个0.4 mm节距的BGA封装DSP芯片摆在板子中央&#xff0c;引脚密密麻麻像…

作者头像 李华
网站建设 2026/4/5 16:17:39

数据预处理自动化:lora-scripts内置工具提升准备效率

数据预处理自动化&#xff1a;lora-scripts内置工具提升准备效率 在如今 AI 模型遍地开花的时代&#xff0c;谁还愿意花三天时间标注 200 张图只为训练一个风格 LoRA&#xff1f;更别提配置环境、调参、解决显存溢出……这些琐碎又致命的细节&#xff0c;往往让一次创意尝试还没…

作者头像 李华
网站建设 2026/3/30 21:39:07

数据清洗必要性说明:提升lora-scripts训练收敛速度的关键

数据清洗&#xff1a;决定 lora-scripts 训练成败的隐形关键 在如今人人都能“微调一个专属模型”的时代&#xff0c;LoRA 技术凭借其轻量、高效的特点迅速走红。无论是想训练一个特定画风的图像生成器&#xff0c;还是定制某个角色形象&#xff0c;只需几十张图片和一台消费级…

作者头像 李华