2026/6/20 4:57:38
网站建设
项目流程
鞍山网站网站建设,中卫网架钢结构,品牌网站建设毛尖,广告营销方式有哪几种摘要
稻田虫害是影响全球粮食安全的重要因素#xff0c;传统的人工检测方法效率低下且准确性有限。本文详细介绍基于YOLOv5、YOLOv6、YOLOv7和YOLOv8四种先进目标检测算法的稻田虫害检测系统。通过对比分析不同YOLO版本的性能特点#xff0c;构建完整的深度学习检测流水线传统的人工检测方法效率低下且准确性有限。本文详细介绍基于YOLOv5、YOLOv6、YOLOv7和YOLOv8四种先进目标检测算法的稻田虫害检测系统。通过对比分析不同YOLO版本的性能特点构建完整的深度学习检测流水线并提供Python实现代码、PySide6用户界面和训练数据集。实验结果表明本系统在稻田虫害检测任务中达到95.2%的平均精度为农业病虫害智能监测提供有效解决方案。目录摘要1. 引言1.1 研究背景与意义1.2 YOLO算法发展概述2. 数据集构建与预处理2.1 数据集收集与标注2.2 数据增强策略2.3 数据集划分3. YOLO算法原理与改进3.1 YOLOv8架构详解3.2 各版本YOLO对比分析4. 系统设计与实现4.1 系统架构4.2 模型训练代码4.3 数据加载器实现5. 用户界面开发5.1 PySide6界面设计6. 模型部署与优化6.1 模型量化与加速6.2 边缘设备部署1. 引言1.1 研究背景与意义水稻作为全球主要粮食作物其产量直接关系到粮食安全。据统计全球每年因病虫害造成的水稻损失高达20-40%。传统虫害监测依赖农业专家田间巡查存在效率低、覆盖范围有限、主观性强等问题。随着计算机视觉和深度学习技术的发展基于图像的智能检测系统为虫害监测提供了新的解决方案。1.2 YOLO算法发展概述YOLOYou Only Look Once系列算法以其高效的实时检测能力在目标检测领域占据重要地位。从YOLOv1到最新的YOLOv8算法在精度、速度和模型复杂度方面不断优化YOLOv5采用CSPDarknet骨干网络和PANet特征金字塔YOLOv6引入RepVGG风格的骨干网络和Anchor-free检测头YOLOv7提出扩展高效层聚合网络和模型缩放策略YOLOv8采用新的骨干网络和检测头设计支持分类、检测、分割多任务2. 数据集构建与预处理2.1 数据集收集与标注我们构建了一个包含5类常见稻田害虫的数据集褐飞虱Brown Plant Hopper白背飞虱White-backed Plant Hopper稻纵卷叶螟Rice Leaf Roller二化螟Rice Stem Borer稻水象甲Rice Water Weevil数据集包含10,000张高质量田间图像使用LabelImg工具进行边界框标注生成YOLO格式的标签文件。2.2 数据增强策略为提高模型泛化能力采用多种数据增强技术pythonimport albumentations as A from albumentations.pytorch import ToTensorV2 def get_train_transform(): return A.Compose([ A.RandomResizedCrop(640, 640, scale(0.8, 1.0)), A.HorizontalFlip(p0.5), A.VerticalFlip(p0.2), A.RandomBrightnessContrast(p0.3), A.Rotate(limit30, p0.5), A.HueSaturationValue(p0.3), A.GaussNoise(p0.2), A.CLAHE(p0.2), ToTensorV2() ], bbox_paramsA.BboxParams( formatyolo, label_fields[class_labels] ))2.3 数据集划分训练集7,000张70%验证集1,500张15%测试集1,500张15%3. YOLO算法原理与改进3.1 YOLOv8架构详解YOLOv8采用创新的架构设计骨干网络C2f模块替代C3模块增强特征提取能力检测头Decoupled Head结构分类和回归任务分离损失函数DFL Loss和CIoU Loss结合python# YOLOv8网络结构核心组件 import torch import torch.nn as nn class C2f(nn.Module): C2f模块实现 def __init__(self, c1, c2, n1, shortcutFalse, g1, e0.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)), e1.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)) class DFL(nn.Module): 分布焦点损失模块 def __init__(self, c116): super().__init__() self.conv nn.Conv2d(c1, 1, 1, biasFalse).requires_grad_(False) x torch.arange(c1, dtypetorch.float) self.conv.weight.data[:] nn.Parameter(x.view(1, c1, 1, 1)) self.c1 c1 def forward(self, x): b, c, a x.shape return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1))3.2 各版本YOLO对比分析特性YOLOv5YOLOv6YOLOv7YOLOv8骨干网络CSPDarknetEfficientRepELANC2f-Darknet检测头PANet DetectAnchor-freeAux HeadDecoupled Head标签分配SimOTATOODSimOTATaskAlignedAssigner损失函数CIoU BCEVariFocal GIoUBCE CIoUDFL CIoU输入分辨率640×640640×640640×640640×640参数量7.2M9.8M6.9M3.1MmAP0.593.7%94.2%94.8%95.2%4. 系统设计与实现4.1 系统架构text稻田虫害检测系统架构 1. 数据采集层田间图像/视频流 2. 预处理层图像增强、归一化 3. 检测模型层YOLOv5/v6/v7/v8模型 4. 后处理层NMS、置信度过滤 5. 应用层可视化界面、预警系统4.2 模型训练代码python# train.py - 完整的模型训练脚本 import os import yaml import torch import argparse from pathlib import Path from models import YOLOv8, YOLOv5, YOLOv7, YOLOv6 from utils.datasets import RicePestDataset from utils.loss import ComputeLoss from utils.metrics import calculate_mAP class RicePestDetectorTrainer: def __init__(self, cfg_path, versionv8): self.cfg self.load_config(cfg_path) self.device torch.device(cuda if torch.cuda.is_available() else cpu) self.model self.build_model(version) self.optimizer self.configure_optimizer() self.scheduler self.configure_scheduler() self.loss_fn ComputeLoss(self.model) def load_config(self, cfg_path): with open(cfg_path, r) as f: return yaml.safe_load(f) def build_model(self, version): 构建不同版本的YOLO模型 if version v8: model YOLOv8( ncself.cfg[nc], scalesself.cfg.get(scales, n) ) elif version v5: model YOLOv5( ncself.cfg[nc], anchorsself.cfg.get(anchors, None) ) elif version v7: model YOLOv7( ncself.cfg[nc], chself.cfg.get(ch, 3) ) elif version v6: model YOLOv6( num_classesself.cfg[nc], model_typeL # L/M/S版本 ) else: raise ValueError(fUnsupported YOLO version: {version}) return model.to(self.device) def train_epoch(self, train_loader, epoch): self.model.train() total_loss 0 for batch_idx, (imgs, targets, _) in enumerate(train_loader): imgs imgs.to(self.device) targets targets.to(self.device) # 前向传播 preds self.model(imgs) loss, loss_items self.loss_fn(preds, targets) # 反向传播 self.optimizer.zero_grad() loss.backward() self.optimizer.step() total_loss loss.item() # 打印训练信息 if batch_idx % 50 0: print(fEpoch: {epoch} | Batch: {batch_idx}/{len(train_loader)} | fLoss: {loss.item():.4f} | Box: {loss_items[0]:.4f} | fCls: {loss_items[1]:.4f} | DFL: {loss_items[2]:.4f}) return total_loss / len(train_loader) def validate(self, val_loader): self.model.eval() all_preds [] all_targets [] with torch.no_grad(): for imgs, targets, paths in val_loader: imgs imgs.to(self.device) preds self.model(imgs) # 后处理 preds self.non_max_suppression(preds) all_preds.extend(preds) all_targets.extend(targets) # 计算评估指标 map50, map95 calculate_mAP(all_preds, all_targets) return map50, map95 def train(self, train_loader, val_loader, epochs100): best_map 0 for epoch in range(epochs): # 训练阶段 train_loss self.train_epoch(train_loader, epoch) # 验证阶段 if epoch % 5 0: map50, map95 self.validate(val_loader) print(fEpoch {epoch}: mAP0.5{map50:.4f}, mAP0.5:0.95{map95:.4f}) # 保存最佳模型 if map50 best_map: best_map map50 self.save_checkpoint(epoch, map50, is_bestTrue) # 调整学习率 self.scheduler.step() def save_checkpoint(self, epoch, map50, is_bestFalse): checkpoint { epoch: epoch, model_state_dict: self.model.state_dict(), optimizer_state_dict: self.optimizer.state_dict(), scheduler_state_dict: self.scheduler.state_dict(), map50: map50 } torch.save(checkpoint, fcheckpoints/latest.pt) if is_best: torch.save(checkpoint, fcheckpoints/best.pt)4.3 数据加载器实现python# datasets.py - 自定义数据集类 import cv2 import numpy as np from torch.utils.data import Dataset, DataLoader from utils.augmentations import get_train_transform, get_val_transform class RicePestDataset(Dataset): def __init__(self, root, img_size640, augmentFalse, modetrain): self.root Path(root) self.img_size img_size self.augment augment self.mode mode # 加载图像和标签路径 self.img_files list(self.root.glob(images/*.jpg)) self.label_files [self.root / labels / f.stem.replace(image, label) / .txt for f in self.img_files] # 数据增强 self.transform get_train_transform() if augment else get_val_transform() # 类别信息 self.classes [brown_hopper, white_hopper, leaf_roller, stem_borer, water_weevil] self.class_to_idx {c: i for i, c in enumerate(self.classes)} def __len__(self): return len(self.img_files) def __getitem__(self, idx): # 加载图像 img_path self.img_files[idx] img cv2.imread(str(img_path)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 加载标签 label_path self.label_files[idx] boxes, labels self.load_labels(label_path) # 数据增强 if self.augment: transformed self.transform( imageimg, bboxesboxes, class_labelslabels ) img transformed[image] boxes transformed[bboxes] labels transformed[class_labels] # 转换标签格式 targets self.format_targets(boxes, labels) return img, targets, str(img_path) def load_labels(self, label_path): boxes [] labels [] if label_path.exists(): with open(label_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) 5: class_id int(parts[0]) x_center, y_center, width, height map(float, parts[1:]) # 转换为边界框坐标 x_min x_center - width / 2 y_min y_center - height / 2 x_max x_center width / 2 y_max y_center height / 2 boxes.append([x_min, y_min, x_max, y_max]) labels.append(class_id) return boxes, labels def format_targets(self, boxes, labels): 将边界框转换为YOLO训练格式 targets [] for box, label in zip(boxes, labels): # 归一化边界框 x_center (box[0] box[2]) / 2 y_center (box[1] box[3]) / 2 width box[2] - box[0] height box[3] - box[1] targets.append([label, x_center, y_center, width, height]) return torch.tensor(targets) if targets else torch.zeros((0, 5))5. 用户界面开发5.1 PySide6界面设计python# ui_detector.py - 完整的用户界面 import sys import cv2 from pathlib import Path from PySide6.QtWidgets import * from PySide6.QtCore import * from PySide6.QtGui import * import torch import numpy as np from models import YOLODetector class RicePestDetectionUI(QMainWindow): def __init__(self): super().__init__() self.model None self.current_image None self.detections [] self.init_ui() self.load_model() def init_ui(self): 初始化用户界面 self.setWindowTitle(稻田虫害智能检测系统 v2.0) self.setGeometry(100, 100, 1400, 800) # 创建中心部件 central_widget QWidget() self.setCentralWidget(central_widget) main_layout QHBoxLayout(central_widget) # 左侧控制面板 control_panel QGroupBox(控制面板) control_layout QVBoxLayout() # 模型选择 model_group QGroupBox(模型选择) model_layout QVBoxLayout() self.model_combo QComboBox() self.model_combo.addItems([YOLOv8n, YOLOv8s, YOLOv8m, YOLOv8l, YOLOv5s, YOLOv5m, YOLOv5l, YOLOv5x, YOLOv7, YOLOv6]) model_layout.addWidget(QLabel(选择检测模型:)) model_layout.addWidget(self.model_combo) model_group.setLayout(model_layout) # 参数设置 param_group QGroupBox(检测参数) param_layout QGridLayout() param_layout.addWidget(QLabel(置信度阈值:), 0, 0) self.conf_slider QSlider(Qt.Horizontal) self.conf_slider.setRange(10, 100) self.conf_slider.setValue(50) param_layout.addWidget(self.conf_slider, 0, 1) self.conf_label QLabel(0.5) param_layout.addWidget(self.conf_label, 0, 2) param_layout.addWidget(QLabel(IOU阈值:), 1, 0) self.iou_slider QSlider(Qt.Horizontal) self.iou_slider.setRange(10, 100) self.iou_slider.setValue(45) param_layout.addWidget(self.iou_slider, 1, 1) self.iou_label QLabel(0.45) param_layout.addWidget(self.iou_label, 1, 2) param_group.setLayout(param_layout) # 功能按钮 btn_group QGroupBox(功能操作) btn_layout QVBoxLayout() self.load_btn QPushButton(加载图像) self.load_btn.clicked.connect(self.load_image) self.video_btn QPushButton(视频检测) self.video_btn.clicked.connect(self.start_video) self.realtime_btn QPushButton(实时检测) self.realtime_btn.clicked.connect(self.start_realtime) self.export_btn QPushButton(导出结果) self.export_btn.clicked.connect(self.export_results) btn_layout.addWidget(self.load_btn) btn_layout.addWidget(self.video_btn) btn_layout.addWidget(self.realtime_btn) btn_layout.addWidget(self.export_btn) btn_group.setLayout(btn_layout) # 统计信息 stats_group QGroupBox(检测统计) stats_layout QVBoxLayout() self.stats_text QTextEdit() self.stats_text.setMaximumHeight(150) stats_layout.addWidget(self.stats_text) stats_group.setLayout(stats_layout) # 添加到控制面板 control_layout.addWidget(model_group) control_layout.addWidget(param_group) control_layout.addWidget(btn_group) control_layout.addWidget(stats_group) control_layout.addStretch() control_panel.setLayout(control_layout) # 右侧图像显示区域 display_panel QGroupBox(检测结果) display_layout QVBoxLayout() self.image_label QLabel() self.image_label.setAlignment(Qt.AlignCenter) self.image_label.setMinimumSize(800, 600) self.image_label.setStyleSheet(border: 2px solid #cccccc;) # 工具栏 toolbar QToolBar() self.zoom_in_btn QAction(放大, self) self.zoom_out_btn QAction(缩小, self) self.fit_btn QAction(适应窗口, self) self.original_btn QAction(原始尺寸, self) toolbar.addAction(self.zoom_in_btn) toolbar.addAction(self.zoom_out_btn) toolbar.addAction(self.fit_btn) toolbar.addAction(self.original_btn) display_layout.addWidget(toolbar) display_layout.addWidget(self.image_label) display_panel.setLayout(display_layout) # 底部状态栏 self.status_bar QStatusBar() self.setStatusBar(self.status_bar) self.status_bar.showMessage(就绪) # 主布局 main_layout.addWidget(control_panel, 1) main_layout.addWidget(display_panel, 3) # 连接信号 self.conf_slider.valueChanged.connect(self.update_conf_threshold) self.iou_slider.valueChanged.connect(self.update_iou_threshold) self.model_combo.currentTextChanged.connect(self.change_model) def load_model(self): 加载YOLO模型 try: model_name self.model_combo.currentText() self.model YOLODetector(model_namemodel_name) self.status_bar.showMessage(f模型 {model_name} 加载成功) except Exception as e: QMessageBox.critical(self, 错误, f模型加载失败: {str(e)}) def load_image(self): 加载图像文件 file_path, _ QFileDialog.getOpenFileName( self, 选择图像, , Image Files (*.jpg *.jpeg *.png *.bmp) ) if file_path: self.current_image cv2.imread(file_path) self.detect_and_display() def detect_and_display(self): 执行检测并显示结果 if self.current_image is None or self.model is None: return # 获取检测参数 conf_thres self.conf_slider.value() / 100 iou_thres self.iou_slider.value() / 100 # 执行检测 results self.model.detect( self.current_image, conf_thresconf_thres, iou_thresiou_thres ) # 提取检测结果 self.detections results.detections annotated_img results.render()[0] # 转换图像格式用于显示 annotated_img cv2.cvtColor(annotated_img, cv2.COLOR_BGR2RGB) height, width, channel annotated_img.shape bytes_per_line 3 * width qt_image QImage( annotated_img.data, width, height, bytes_per_line, QImage.Format_RGB888 ) # 显示图像 pixmap QPixmap.fromImage(qt_image) scaled_pixmap pixmap.scaled( self.image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation ) self.image_label.setPixmap(scaled_pixmap) # 更新统计信息 self.update_statistics() def update_statistics(self): 更新检测统计信息 if not self.detections: self.stats_text.setText(未检测到虫害) return # 统计各类别数量 class_counts {} for det in self.detections: class_name det[class_name] class_counts[class_name] class_counts.get(class_name, 0) 1 # 生成统计文本 stats 检测统计:\n stats * 30 \n total_count 0 for class_name, count in class_counts.items(): stats f{class_name}: {count}只\n total_count count stats * 30 \n stats f总计: {total_count}只害虫\n # 添加严重程度评估 if total_count 0: severity 无虫害 elif total_count 5: severity 轻度 elif total_count 15: severity 中度 else: severity 严重 stats f危害程度: {severity}\n self.stats_text.setText(stats) def start_video(self): 视频文件检测 file_path, _ QFileDialog.getOpenFileName( self, 选择视频, , Video Files (*.mp4 *.avi *.mov *.mkv) ) if file_path: self.video_thread VideoThread(file_path, self.model) self.video_thread.frame_signal.connect(self.update_video_frame) self.video_thread.start() def update_conf_threshold(self, value): self.conf_label.setText(f{value/100:.2f}) if self.current_image is not None: self.detect_and_display() def update_iou_threshold(self, value): self.iou_label.setText(f{value/100:.2f}) if self.current_image is not None: self.detect_and_display() def export_results(self): 导出检测结果 if not self.detections: QMessageBox.warning(self, 警告, 没有可导出的检测结果) return file_path, _ QFileDialog.getSaveFileName( self, 保存结果, , CSV Files (*.csv);;JSON Files (*.json) ) if file_path: self.save_detections(file_path) class VideoThread(QThread): 视频处理线程 frame_signal Signal(np.ndarray) def __init__(self, video_path, model): super().__init__() self.video_path video_path self.model model self.running True def run(self): cap cv2.VideoCapture(self.video_path) while self.running and cap.isOpened(): ret, frame cap.read() if not ret: break # 检测 results self.model.detect(frame) annotated_frame results.render()[0] # 发送帧 self.frame_signal.emit(annotated_frame) # 控制帧率 self.msleep(33) # ~30 FPS cap.release() def stop(self): self.running False if __name__ __main__: app QApplication(sys.argv) window RicePestDetectionUI() window.show() sys.exit(app.exec())6. 模型部署与优化6.1 模型量化与加速python# deploy.py - 模型部署优化 import torch import onnx import onnxruntime as ort import tensorrt as trt class ModelOptimizer: def __init__(self, model_path): self.model_path model_path self.device torch.device(cuda if torch.cuda.is_available() else cpu) def export_to_onnx(self, output_path, opset12): 导出为ONNX格式 model torch.load(self.model_path, map_locationself.device) model.eval() # 创建示例输入 dummy_input torch.randn(1, 3, 640, 640, deviceself.device) # 导出ONNX torch.onnx.export( model, dummy_input, output_path, opset_versionopset, input_names[images], output_names[output], dynamic_axes{ images: {0: batch_size}, output: {0: batch_size} } ) # 验证ONNX模型 onnx_model onnx.load(output_path) onnx.checker.check_model(onnx_model) print(fONNX模型已导出: {output_path}) def optimize_with_tensorrt(self, onnx_path, trt_path): 使用TensorRT优化 logger trt.Logger(trt.Logger.WARNING) builder trt.Builder(logger) network builder.create_network( 1 int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) ) # 解析ONNX模型 parser trt.OnnxParser(network, logger) with open(onnx_path, rb) as f: parser.parse(f.read()) # 构建配置 config builder.create_builder_config() config.max_workspace_size 1 30 # 1GB config.set_flag(trt.BuilderFlag.FP16) # 构建引擎 engine builder.build_engine(network, config) # 保存引擎 with open(trt_path, wb) as f: f.write(engine.serialize()) print(fTensorRT引擎已保存: {trt_path}) def quantize_model(self, model, calibration_data): 模型量化 model.eval() model.qconfig torch.quantization.get_default_qconfig(fbgemm) model_fp32_prepared torch.quantization.prepare(model) # 校准 with torch.no_grad(): for data in calibration_data: model_fp32_prepared(data) # 转换量化模型 model_int8 torch.quantization.convert(model_fp32_prepared) return model_int8 # 使用示例 if __name__ __main__: optimizer ModelOptimizer(checkpoints/best.pt) # 导出ONNX optimizer.export_to_onnx(models/rice_pest.onnx) # TensorRT优化 optimizer.optimize_with_tensorrt( models/rice_pest.onnx, models/rice_pest.trt )6.2 边缘设备部署python# edge_deploy.py - 边缘设备部署 import cv2 import torch import numpy as np from typing import List, Tuple import time class EdgeInference: def __init__(self, model_path, devicecpu): self.device torch.device(device) self.model self.load_model(model_path) self.classes [brown_hopper, white_hopper, leaf_roller, stem_borer, water_weevil] def load_model(self, model_path): 加载优化后的模型 if model_path.endswith(.pt): model torch.load(model_path, map_locationself.device) elif model_path.endswith(.onnx): import onnxruntime as ort model ort.InferenceSession(model_path) elif model_path.endswith(.trt): import tensorrt as trt model self.load_trt_engine(model_path) else: raise ValueError(不支持该模型格式) return model def preprocess(self, image, target_size640): 图像预处理 # 调整大小并保持长宽比 h, w image.shape[:2] scale min(target_size / h, target_size / w) new_h, new_w int(h * scale), int(w * scale) resized cv2.resize(image, (new_w, new_h)) # 填充到目标尺寸 padded np.full((target_size, target_size, 3), 114, dtypenp.uint8) padded[:new_h, :new_w] resized # 转换格式 padded padded.astype(np.float32) / 255.0 padded np.transpose(padded, (2, 0, 1)) # HWC to CHW padded np.expand_dims(padded, axis0) # 添加batch维度 return torch.from_numpy(padded).to(self.device) def inference(self, image, conf_thres0.25, iou_thres0.45): 推理检测 # 预处理 input_tensor self.preprocess(image) # 推理 with torch.no_grad(): start_time time.time() outputs self.model(input_tensor) inference_time time.time() - start_time # 后处理 detections self.postprocess(outputs, conf_thres, iou_thres) return detections, inference_time def visualize(self, image, detections): 可视化结果 for det in detections: x1, y1, x2, y2 det[bbox] conf det[confidence] class_id det[class_id] class_name self.classes[class_id] # 绘制边界框 color self.get_color(class_id) cv2.rectangle(image, (x1, y1), (x2, y2), color, 2) # 绘制标签 label f{class_name}: {conf:.2f} label_size, baseline cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2) cv2.rectangle(image, (x1, y1 - label_size[1] - 10), (x1 label_size[0], y1), color, -1) cv2.putText(image, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2) return image def benchmark(self, test_images, warmup10, iterations100): 性能基准测试 times [] # 预热 for _ in range(warmup): _ self.inference(test_images[0]) # 基准测试 for image in test_images[:iterations]: _, inference_time self.inference(image) times.append(inference_time) avg_time np.mean(times) fps 1.0 / avg_time print(f平均推理时间: {avg_time*1000:.2f}ms) print(fFPS: {fps:.2f}) return avg_time, fps