2026/1/25 1:48:51
网站建设
项目流程
自己的电脑做网站服务器 买的服务器 速度,域名注册信息可以在哪里找到,济南建设网点电话,企业网属于什么网摘要随着城市汽车保有量的快速增长#xff0c;停车难问题日益凸显。传统的停车位检测方法通常依赖于近距离传感器或人工巡查#xff0c;存在效率低、覆盖范围有限等问题。本文提出了一种基于YOLOv5/v6/v7/v8深度学习模型的远距离停车位检测系统#xff0c;能够从高空视角或较…摘要随着城市汽车保有量的快速增长停车难问题日益凸显。传统的停车位检测方法通常依赖于近距离传感器或人工巡查存在效率低、覆盖范围有限等问题。本文提出了一种基于YOLOv5/v6/v7/v8深度学习模型的远距离停车位检测系统能够从高空视角或较远距离准确识别停车位占用状态。系统采用改进的YOLO算法结合自定义UI界面和高效的数据处理流程实现了对大面积停车场的实时监控。本文将详细阐述系统架构、算法优化、数据集构建、训练策略以及完整实现代码为智慧城市建设提供技术参考。1. 引言1.1 研究背景在城市交通管理领域停车位检测是智能交通系统的重要组成部分。传统的停车检测方法包括地磁传感器、超声波传感器、摄像头定点监控等但这些方法存在部署成本高、维护困难、覆盖范围有限等缺点。随着计算机视觉技术的发展基于深度学习的视觉检测方法逐渐成为主流解决方案。1.2 YOLO算法发展概述YOLOYou Only Look Once系列算法自2015年提出以来经历了多个版本的迭代优化YOLOv5Ultralytics公司推出的工业级实现以易用性和高性能著称YOLOv6美团视觉智能部推出的专注于工业应用的目标检测框架YOLOv7在速度和精度上达到新的平衡引入高效层聚合网络YOLOv8最新版本提供更灵活的架构和更好的性能表现1.3 系统创新点本系统的创新之处在于远距离检测能力优化网络结构以处理远距离、小目标检测多版本YOLO支持兼容主流YOLO版本便于对比和选择完整系统集成包含数据预处理、模型训练、推理部署和UI界面实际场景优化针对停车场特定场景进行算法优化2. 系统架构设计2.1 整体架构text┌─────────────────────────────────────────────────────┐ │ 用户界面层 │ │ (PyQt5/Tkinter Web界面) │ ├─────────────────────────────────────────────────────┤ │ 应用服务层 │ │ (检测引擎、结果处理、数据存储) │ ├─────────────────────────────────────────────────────┤ │ 深度学习模型层 │ │ (YOLOv5/v6/v7/v8模型加载与推理) │ ├─────────────────────────────────────────────────────┤ │ 数据处理层 │ │ (图像预处理、数据增强、后处理) │ ├─────────────────────────────────────────────────────┤ │ 硬件支持层 │ │ (GPU加速、摄像头接入、云平台) │ └─────────────────────────────────────────────────────┘2.2 技术栈深度学习框架PyTorch 1.7模型实现YOLOv5/v6/v7/v8界面开发PyQt5/Tkinter数据处理OpenCV, Pillow, NumPy部署工具ONNX, TensorRT (可选)3. 数据集准备与处理3.1 参考数据集PKLot数据集包含约700,000张停车位图像标注精细CNRParkEXT数据集意大利帕尔马大学提供涵盖不同天气条件Smart Parking Dataset包含不同角度和距离的停车场景自定义数据集针对特定场景采集的数据3.2 数据标注规范使用LabelImg或CVAT工具进行标注类别包括occupied车位被占用vacant空车位partially_occupied部分占用如摩托车标注格式采用YOLO格式textclass_id x_center y_center width height3.3 数据增强策略针对远距离检测特点采用以下增强方法pythonimport albumentations as A from albumentations.pytorch import ToTensorV2 def get_train_transforms(image_size640): return A.Compose([ A.RandomResizedCrop(heightimage_size, widthimage_size, scale(0.5, 1.0)), A.HorizontalFlip(p0.5), A.VerticalFlip(p0.1), A.RandomBrightnessContrast(p0.3), A.HueSaturationValue(p0.3), A.GaussNoise(p0.1), A.CLAHE(p0.1), A.RandomGamma(p0.1), A.ImageCompression(quality_lower70, quality_upper100, p0.2), A.OneOf([ A.MotionBlur(p0.2), A.MedianBlur(blur_limit3, p0.1), A.Blur(blur_limit3, p0.1), ], p0.2), A.ShiftScaleRotate(shift_limit0.0625, scale_limit0.2, rotate_limit10, p0.5), ToTensorV2(p1.0) ], bbox_paramsA.BboxParams( formatyolo, label_fields[class_labels], min_visibility0.3 )) def get_val_transforms(image_size640): return A.Compose([ A.Resize(heightimage_size, widthimage_size), ToTensorV2(p1.0) ], bbox_paramsA.BboxParams( formatyolo, label_fields[class_labels] ))4. YOLO模型实现与优化4.1 YOLOv5/v6/v7/v8统一接口设计pythonimport torch import numpy as np from pathlib import Path from typing import List, Tuple, Dict, Optional import cv2 class YOLOParkDetector: def __init__(self, model_typeyolov8, model_pathNone, devicecuda): 初始化YOLO停车检测器 Args: model_type: 模型类型 (yolov5, yolov6, yolov7, yolov8) model_path: 模型权重路径 device: 运行设备 self.model_type model_type self.device device if torch.cuda.is_available() and device cuda else cpu self.model self._load_model(model_path) self.class_names [occupied, vacant, partially_occupied] self.colors [(0, 0, 255), (0, 255, 0), (255, 255, 0)] def _load_model(self, model_path): 加载YOLO模型 if self.model_type yolov5: return self._load_yolov5(model_path) elif self.model_type yolov6: return self._load_yolov6(model_path) elif self.model_type yolov7: return self._load_yolov7(model_path) elif self.model_type yolov8: return self._load_yolov8(model_path) else: raise ValueError(fUnsupported model type: {self.model_type}) def _load_yolov5(self, model_path): 加载YOLOv5模型 from models.yolov5 import Model # 加载自定义YOLOv5模型 model torch.hub.load(ultralytics/yolov5, custom, pathmodel_path, force_reloadTrue) model.to(self.device) model.eval() return model def _load_yolov8(self, model_path): 加载YOLOv8模型 from ultralytics import YOLO model YOLO(model_path) return model def preprocess(self, image: np.ndarray, img_size: int 640): 图像预处理 # 保持宽高比的resize h, w image.shape[:2] scale min(img_size / h, img_size / w) new_h, new_w int(h * scale), int(w * scale) resized cv2.resize(image, (new_w, new_h)) # 填充到正方形 padded np.full((img_size, img_size, 3), 114, dtypenp.uint8) padded[:new_h, :new_w] resized # 归一化 padded padded.astype(np.float32) / 255.0 # 转换为torch tensor tensor torch.from_numpy(padded).permute(2, 0, 1).unsqueeze(0) return tensor.to(self.device), (scale, (img_size - new_w) // 2, (img_size - new_h) // 2) def postprocess(self, predictions, orig_shape, params, conf_thresh0.25, iou_thresh0.45): 后处理NMS和坐标转换 scale, pad_x, pad_y params if self.model_type yolov8: # YOLOv8输出处理 boxes predictions[0].boxes if len(boxes) 0: return [] detections [] for box in boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy() conf box.conf[0].cpu().numpy() cls int(box.cls[0].cpu().numpy()) # 去除填充并还原到原始尺寸 x1 max(0, (x1 - pad_x) / scale) y1 max(0, (y1 - pad_y) / scale) x2 min(orig_shape[1], (x2 - pad_x) / scale) y2 min(orig_shape[0], (y2 - pad_y) / scale) if conf conf_thresh: detections.append({ bbox: [x1, y1, x2, y2], confidence: float(conf), class_id: cls, class_name: self.class_names[cls] }) else: # 其他YOLO版本处理 # ... 具体实现 return detections def detect(self, image: np.ndarray, conf_thresh0.25): 执行检测 # 预处理 tensor, params self.preprocess(image) # 推理 with torch.no_grad(): if self.model_type yolov8: results self.model(tensor) predictions results else: predictions self.model(tensor) # 后处理 detections self.postprocess(predictions, image.shape[:2], params, conf_thresh) return detections4.2 针对远距离检测的优化4.2.1 小目标检测增强pythonclass EnhancedYOLO(nn.Module): def __init__(self, base_model): super().__init__() self.base_model base_model # 添加小目标检测头 self.small_object_head nn.Sequential( nn.Conv2d(256, 512, 3, padding1), nn.BatchNorm2d(512), nn.LeakyReLU(0.1), nn.Conv2d(512, 256, 3, padding1), nn.BatchNorm2d(256), nn.LeakyReLU(0.1), nn.Conv2d(256, 3 * (5 len(classes)), 1) # 3个anchor ) # 注意力机制 self.cbam CBAM(512) def forward(self, x): features self.base_model.backbone(x) # 应用注意力 enhanced_features self.cbam(features[-1]) # 小目标检测 small_obj_out self.small_object_head(enhanced_features) # 与原始输出融合 outputs self.base_model.head(features) outputs.append(small_obj_out) return outputs class CBAM(nn.Module): Convolutional Block Attention Module def __init__(self, channels, reduction16): super().__init__() self.channel_attention nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(channels, channels // reduction, 1), nn.ReLU(), nn.Conv2d(channels // reduction, channels, 1), nn.Sigmoid() ) self.spatial_attention nn.Sequential( nn.Conv2d(2, 1, 7, padding3), nn.Sigmoid() ) def forward(self, x): # 通道注意力 ca self.channel_attention(x) x_ca x * ca # 空间注意力 avg_pool torch.mean(x_ca, dim1, keepdimTrue) max_pool torch.max(x_ca, dim1, keepdimTrue)[0] concat torch.cat([avg_pool, max_pool], dim1) sa self.spatial_attention(concat) x_sa x_ca * sa return x_sa4.2.2 多尺度特征融合pythonclass MultiScaleFusion(nn.Module): 多尺度特征融合模块 def __init__(self, in_channels): super().__init__() # 特征金字塔 self.lateral_convs nn.ModuleList() self.fpn_convs nn.ModuleList() for i in range(len(in_channels)): lateral_conv nn.Conv2d(in_channels[i], 256, 1) fpn_conv nn.Sequential( nn.Conv2d(256, 256, 3, padding1), nn.BatchNorm2d(256), nn.ReLU() ) self.lateral_convs.append(lateral_conv) self.fpn_convs.append(fpn_conv) def forward(self, features): # 自底向上 laterals [conv(f) for conv, f in zip(self.lateral_convs, features)] # 自顶向下 used_features [laterals[-1]] for i in range(len(laterals)-2, -1, -1): lateral laterals[i] top_down F.interpolate( used_features[-1], sizelateral.shape[2:], modenearest ) fused lateral top_down used_features.append(fused) used_features used_features[::-1] # FPN输出 outputs [] for i, (conv, feat) in enumerate(zip(self.fpn_convs, used_features)): outputs.append(conv(feat)) return outputs5. 模型训练策略5.1 训练配置pythonimport yaml from torch.optim import AdamW, SGD from torch.optim.lr_scheduler import CosineAnnealingLR, OneCycleLR class ParkingTrainer: def __init__(self, config_path): with open(config_path, r) as f: self.config yaml.safe_load(f) self.setup_hyperparameters() def setup_hyperparameters(self): 设置训练超参数 self.hyperparams { lr0: 0.01, # 初始学习率 lrf: 0.01, # 最终学习率系数 momentum: 0.937, weight_decay: 0.0005, warmup_epochs: 3, warmup_momentum: 0.8, warmup_bias_lr: 0.1, box: 0.05, # box损失权重 cls: 0.5, # 分类损失权重 cls_pw: 1.0, # 分类正样本权重 obj: 1.0, # 目标损失权重 obj_pw: 1.0, # 目标正样本权重 iou_t: 0.20, # IoU阈值 anchor_t: 4.0, # anchor阈值 fl_gamma: 0.0, # Focal loss gamma } def create_optimizer(self, model): 创建优化器 optimizer AdamW( model.parameters(), lrself.hyperparams[lr0], weight_decayself.hyperparams[weight_decay] ) scheduler CosineAnnealingLR( optimizer, T_maxself.config[epochs], eta_minself.hyperparams[lr0] * self.hyperparams[lrf] ) return optimizer, scheduler def compute_loss(self, predictions, targets): 计算损失函数 # 回归损失 giou_loss self.compute_giou_loss(predictions[..., :4], targets[..., :4]) # 置信度损失 obj_loss self.compute_obj_loss(predictions[..., 4], targets[..., 4]) # 分类损失 cls_loss self.compute_cls_loss(predictions[..., 5:], targets[..., 5]) total_loss ( self.hyperparams[box] * giou_loss self.hyperparams[obj] * obj_loss self.hyperparams[cls] * cls_loss ) return total_loss, {giou: giou_loss, obj: obj_loss, cls: cls_loss} def compute_giou_loss(self, pred_boxes, target_boxes): 计算GIoU损失 # GIoU实现 pass5.2 训练脚本pythonimport argparse import torch from torch.utils.data import DataLoader from tqdm import tqdm import wandb def train(model, train_loader, val_loader, config, device): 训练主函数 # 初始化wandb if config.use_wandb: wandb.init(projectparking-detection, configconfig) # 优化器 optimizer, scheduler ParkingTrainer(config).create_optimizer(model) # 训练循环 best_map 0.0 for epoch in range(config.epochs): # 训练阶段 model.train() train_loss 0.0 progress_bar tqdm(train_loader, descfEpoch {epoch1}/{config.epochs}) for batch_idx, (images, targets) in enumerate(progress_bar): images images.to(device) targets targets.to(device) # 前向传播 predictions model(images) loss, loss_dict trainer.compute_loss(predictions, targets) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() # 更新进度条 train_loss loss.item() progress_bar.set_postfix({ loss: loss.item(), giou: loss_dict[giou].item(), cls: loss_dict[cls].item() }) # wandb记录 if config.use_wandb and batch_idx % config.log_interval 0: wandb.log({ train/loss: loss.item(), train/giou_loss: loss_dict[giou].item(), train/cls_loss: loss_dict[cls].item(), lr: optimizer.param_groups[0][lr] }) # 学习率调整 scheduler.step() # 验证阶段 if (epoch 1) % config.val_interval 0: val_metrics validate(model, val_loader, device, config) # 保存最佳模型 if val_metrics[mAP0.5] best_map: best_map val_metrics[mAP0.5] torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), best_map: best_map, config: config }, fcheckpoints/best_model.pth) # wandb记录验证指标 if config.use_wandb: wandb.log({ val/mAP: val_metrics[mAP0.5], val/mAP0.5:0.95: val_metrics[mAP0.5:0.95], val/precision: val_metrics[precision], val/recall: val_metrics[recall] }) return model def validate(model, val_loader, device, config): 验证函数 model.eval() all_predictions [] all_targets [] with torch.no_grad(): for images, targets in tqdm(val_loader, descValidating): images images.to(device) predictions model(images) # 后处理 detections non_max_suppression(predictions, conf_thresconfig.conf_thres, iou_thresconfig.iou_thres) all_predictions.extend(detections) all_targets.extend(targets) # 计算指标 metrics compute_metrics(all_predictions, all_targets, config) return metrics6. UI界面设计6.1 PyQt5主界面pythonimport sys from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog, QComboBox, QSlider, QSpinBox, QGroupBox, QTextEdit, QTabWidget, QTableWidget, QTableWidgetItem) from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QThread from PyQt5.QtGui import QImage, QPixmap, QFont import cv2 import numpy as np class DetectionThread(QThread): 检测线程 detection_done pyqtSignal(list, np.ndarray) def __init__(self, detector, image): super().__init__() self.detector detector self.image image def run(self): detections self.detector.detect(self.image) self.detection_done.emit(detections, self.image) class ParkingDetectionGUI(QMainWindow): def __init__(self, config): super().__init__() self.config config self.detector None self.current_image None self.camera None self.init_ui() self.init_detector() def init_ui(self): 初始化UI self.setWindowTitle(远距离停车位检测系统 v1.0) self.setGeometry(100, 100, 1400, 800) # 中心部件 central_widget QWidget() self.setCentralWidget(central_widget) # 主布局 main_layout QHBoxLayout(central_widget) # 左侧面板 - 图像显示 left_panel QWidget() left_layout QVBoxLayout(left_panel) # 图像显示区域 self.image_label QLabel() self.image_label.setAlignment(Qt.AlignCenter) self.image_label.setMinimumSize(800, 600) self.image_label.setStyleSheet(border: 2px solid gray;) left_layout.addWidget(self.image_label) # 控制按钮 control_layout QHBoxLayout() self.load_btn QPushButton(加载图像) self.load_btn.clicked.connect(self.load_image) self.camera_btn QPushButton(开启摄像头) self.camera_btn.clicked.connect(self.toggle_camera) self.detect_btn QPushButton(开始检测) self.detect_btn.clicked.connect(self.start_detection) self.export_btn QPushButton(导出结果) self.export_btn.clicked.connect(self.export_results) control_layout.addWidget(self.load_btn) control_layout.addWidget(self.camera_btn) control_layout.addWidget(self.detect_btn) control_layout.addWidget(self.export_btn) left_layout.addLayout(control_layout) # 右侧面板 - 控制与信息 right_panel QWidget() right_layout QVBoxLayout(right_panel) # 模型选择 model_group QGroupBox(模型设置) model_layout QVBoxLayout() self.model_combo QComboBox() self.model_combo.addItems([YOLOv5, YOLOv6, YOLOv7, YOLOv8]) self.model_combo.currentTextChanged.connect(self.change_model) self.confidence_slider QSlider(Qt.Horizontal) self.confidence_slider.setRange(1, 99) self.confidence_slider.setValue(25) self.confidence_slider.valueChanged.connect(self.update_confidence) self.confidence_label QLabel(置信度阈值: 0.25) model_layout.addWidget(QLabel(选择模型:)) model_layout.addWidget(self.model_combo) model_layout.addWidget(self.confidence_label) model_layout.addWidget(self.confidence_slider) model_group.setLayout(model_layout) right_layout.addWidget(model_group) # 检测结果统计 stats_group QGroupBox(检测统计) stats_layout QVBoxLayout() self.total_label QLabel(总车位: 0) self.occupied_label QLabel(占用: 0) self.vacant_label QLabel(空闲: 0) self.occupancy_label QLabel(占用率: 0%) stats_layout.addWidget(self.total_label) stats_layout.addWidget(self.occupied_label) stats_layout.addWidget(self.vacant_label) stats_layout.addWidget(self.occupancy_label) stats_group.setLayout(stats_layout) right_layout.addWidget(stats_group) # 详细信息表格 info_group QGroupBox(检测详情) info_layout QVBoxLayout() self.detection_table QTableWidget() self.detection_table.setColumnCount(5) self.detection_table.setHorizontalHeaderLabels([ ID, 类型, 置信度, 位置, 状态 ]) info_layout.addWidget(self.detection_table) info_group.setLayout(info_layout) right_layout.addWidget(info_group) # 日志区域 log_group QGroupBox(系统日志) log_layout QVBoxLayout() self.log_text QTextEdit() self.log_text.setReadOnly(True) self.log_text.setMaximumHeight(150) log_layout.addWidget(self.log_text) log_group.setLayout(log_layout) right_layout.addWidget(log_group) # 添加到主布局 main_layout.addWidget(left_panel, 70) main_layout.addWidget(right_panel, 30) # 状态栏 self.statusBar().showMessage(就绪) # 定时器用于摄像头 self.timer QTimer() self.timer.timeout.connect(self.update_camera_frame) def init_detector(self): 初始化检测器 model_type self.model_combo.currentText().lower() model_path fmodels/{model_type}_parking.pt try: self.detector YOLOParkDetector( model_typemodel_type, model_pathmodel_path, devicecuda if torch.cuda.is_available() else cpu ) self.log_message(f成功加载{model_type}模型) except Exception as e: self.log_message(f加载模型失败: {str(e)}) def load_image(self): 加载图像 file_path, _ QFileDialog.getOpenFileName( self, 选择图像, , 图像文件 (*.jpg *.jpeg *.png *.bmp) ) if file_path: self.current_image cv2.imread(file_path) if self.current_image is not None: self.display_image(self.current_image) self.log_message(f已加载图像: {file_path}) def display_image(self, image, detectionsNone): 显示图像和检测结果 display_image image.copy() if detections: for det in detections: bbox det[bbox] class_id det[class_id] confidence det[confidence] # 绘制边界框 color self.detector.colors[class_id] cv2.rectangle(display_image, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), color, 2) # 添加标签 label f{det[class_name]} {confidence:.2f} cv2.putText(display_image, label, (int(bbox[0]), int(bbox[1]) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) # 转换为Qt格式显示 h, w, ch display_image.shape bytes_per_line ch * w qt_image QImage(display_image.data, w, h, bytes_per_line, QImage.Format_RGB888).rgbSwapped() self.image_label.setPixmap(QPixmap.fromImage(qt_image).scaled( self.image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)) def start_detection(self): 开始检测 if self.current_image is None: self.log_message(请先加载图像) return if self.detector is None: self.log_message(检测器未初始化) return # 创建检测线程 self.detection_thread DetectionThread(self.detector, self.current_image) self.detection_thread.detection_done.connect(self.on_detection_done) self.detection_thread.start() self.statusBar().showMessage(检测中...) def on_detection_done(self, detections, image): 检测完成回调 self.display_image(image, detections) self.update_statistics(detections) self.update_detection_table(detections) self.statusBar().showMessage(检测完成) def update_statistics(self, detections): 更新统计信息 total len(detections) occupied sum(1 for d in detections if d[class_name] occupied) vacant sum(1 for d in detections if d[class_name] vacant) occupancy_rate (occupied / total * 100) if total 0 else 0 self.total_label.setText(f总车位: {total}) self.occupied_label.setText(f占用: {occupied}) self.vacant_label.setText(f空闲: {vacant}) self.occupancy_label.setText(f占用率: {occupancy_rate:.1f}%) def update_detection_table(self, detections): 更新检测表格 self.detection_table.setRowCount(len(detections)) for i, det in enumerate(detections): bbox det[bbox] position f({bbox[0]:.0f}, {bbox[1]:.0f}, {bbox[2]:.0f}, {bbox[3]:.0f}) self.detection_table.setItem(i, 0, QTableWidgetItem(str(i1))) self.detection_table.setItem(i, 1, QTableWidgetItem(det[class_name])) self.detection_table.setItem(i, 2, QTableWidgetItem(f{det[confidence]:.3f})) self.detection_table.setItem(i, 3, QTableWidgetItem(position)) self.detection_table.setItem(i, 4, QTableWidgetItem( 已占用 if det[class_name] occupied else 空闲)) def toggle_camera(self): 切换摄像头 if self.camera is None: self.camera cv2.VideoCapture(0) if self.camera.isOpened(): self.timer.start(30) # 30ms间隔 self.camera_btn.setText(关闭摄像头) self.log_message(摄像头已开启) else: self.timer.stop() self.camera.release() self.camera None self.camera_btn.setText(开启摄像头) self.log_message(摄像头已关闭) def update_camera_frame(self): 更新摄像头帧 if self.camera is not None: ret, frame self.camera.read() if ret: self.current_image frame # 实时检测简化版 if self.detector and self.detect_btn.isChecked(): detections self.detector.detect(frame) self.display_image(frame, detections) self.update_statistics(detections) else: self.display_image(frame) def log_message(self, message): 记录日志 self.log_text.append(f[{QDateTime.currentDateTime().toString(hh:mm:ss)}] {message}) def change_model(self, model_name): 切换模型 self.init_detector() def update_confidence(self, value): 更新置信度阈值 confidence value / 100 self.confidence_label.setText(f置信度阈值: {confidence:.2f}) if self.detector: self.detector.conf_thresh confidence def export_results(self): 导出结果 # 实现导出功能 pass def main(): app QApplication(sys.argv) config { model_path: models/, conf_thres: 0.25, iou_thres: 0.45 } window ParkingDetectionGUI(config) window.show() sys.exit(app.exec_()) if __name__ __main__: main()6.2 Web界面版本可选pythonfrom flask import Flask, render_template, Response, jsonify, request import cv2 import numpy as np import json app Flask(__name__) class WebParkingDetection: def __init__(self): self.detector None self.camera None def init_detector(self): 初始化检测器 # 类似桌面版的实现 pass app.route(/) def index(): return render_template(index.html) app.route(/video_feed) def video_feed(): 视频流 def generate(): camera cv2.VideoCapture(0) while True: success, frame camera.read() if not success: break else: # 检测处理 ret, buffer cv2.imencode(.jpg, frame) frame buffer.tobytes() yield (b--frame\r\n bContent-Type: image/jpeg\r\n\r\n frame b\r\n) return Response(generate(), mimetypemultipart/x-mixed-replace; boundaryframe) app.route(/detect, methods[POST]) def detect(): 检测API if image not in request.files: return jsonify({error: No image provided}), 400 file request.files[image] image cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR) # 执行检测 detections detector.detect(image) return jsonify({ detections: detections, count: len(detections) }) if __name__ __main__: app.run(debugTrue, host0.0.0.0, port5000)7. 系统部署与优化7.1 模型量化与加速pythonimport onnx import onnxruntime as ort import tensorrt as trt class ModelOptimizer: def __init__(self, model_path): self.model_path model_path def to_onnx(self, output_path, input_shape(1, 3, 640, 640)): 转换为ONNX格式 model torch.load(self.model_path, map_locationcpu) model.eval() dummy_input torch.randn(input_shape) torch.onnx.export( model, dummy_input, output_path, export_paramsTrue, opset_version11, do_constant_foldingTrue, input_names[input], output_names[output], dynamic_axes{ input: {0: batch_size}, output: {0: batch_size} } ) # 简化模型 onnx_model onnx.load(output_path) onnx.checker.check_model(onnx_model) return output_path def optimize_with_tensorrt(self, onnx_path, output_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.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 30) # 构建引擎 serialized_engine builder.build_serialized_network(network, config) with open(output_path, wb) as f: f.write(serialized_engine) return output_path def quantize_model(self, model, calibration_data): 模型量化 model.eval() model.qconfig torch.quantization.get_default_qconfig(fbgemm) # 准备量化 torch.quantization.prepare(model, inplaceTrue) # 校准 with torch.no_grad(): for data in calibration_data: model(data) # 转换 torch.quantization.convert(model, inplaceTrue) return model7.2 性能评估pythonimport time from sklearn.metrics import precision_recall_curve, average_precision_score import matplotlib.pyplot as plt class PerformanceEvaluator: def __init__(self, detector, test_dataset): self.detector detector self.test_dataset test_dataset def evaluate_speed(self, num_iterations100): 评估推理速度 times [] for i in range(num_iterations): # 随机选择测试图像 idx np.random.randint(len(self.test_dataset)) image, _ self.test_dataset[idx] # 计时 start_time time.perf_counter() _ self.detector.detect(image) end_time time.perf_counter() times.append(end_time - start_time) avg_time np.mean(times) fps 1.0 / avg_time return { average_inference_time: avg_time, fps: fps, min_time: np.min(times), max_time: np.max(times) } def evaluate_accuracy(self): 评估检测精度 all_predictions [] all_targets [] for image, target in tqdm(self.test_dataset, descEvaluating): predictions self.detector.detect(image) # 转换为评估格式 pred_boxes [p[bbox] for p in predictions] pred_scores [p[confidence] for p in predictions] pred_labels [p[class_id] for p in predictions] all_predictions.append({ boxes: pred_boxes, scores: pred_scores, labels: pred_labels }) all_targets.append(target) # 计算mAP metrics self.compute_map(all_predictions, all_targets) return metrics def compute_map(self, predictions, targets, iou_threshold0.5): 计算mAP # 实现mAP计算逻辑 pass def plot_precision_recall(self, predictions, targets): 绘制PR曲线 # 收集所有预测和真实值 all_pred_scores [] all_pred_labels [] all_true_labels [] for pred, target in zip(predictions, targets): # 处理逻辑 # 计算每个类别的AP ap_per_class {} for class_id in range(num_classes): precision, recall, _ precision_recall_curve( all_true_labels class_id, all_pred_scores[all_pred_labels class_id] ) ap average_precision_score( all_true_labels class_id, all_pred_scores[all_pred_labels class_id] ) ap_per_class[class_id] ap # 绘制曲线 plt.plot(recall, precision, labelfClass {class_id} (AP{ap:.3f})) plt.xlabel(Recall) plt.ylabel(Precision) plt.title(Precision-Recall Curve) plt.legend() plt.grid() return plt.gcf(), ap_per_class