MATLAB R2022a与YOLOv5s实战:打造智能目标检测可视化工具

在计算机视觉领域,目标检测技术正以前所未有的速度改变着我们与数字世界的交互方式。想象一下,你只需轻点鼠标,就能让计算机自动识别画面中的每一个物体——这正是YOLOv5这类先进算法带来的可能性。而MATLAB作为工程计算领域的标杆工具,其强大的App Designer模块让开发者能够轻松构建专业级GUI应用。本文将带你从零开始,将前沿的YOLOv5s模型封装成一个功能完善、界面友好的中文目标检测工具。

1. 开发环境准备与模型部署

1.1 系统要求与软件配置

要顺利运行本项目的完整代码,需要确保开发环境满足以下基本要求:

  • MATLAB版本:R2022a或更高(部分函数在早期版本可能不兼容)
  • 深度学习工具箱:必须安装Deep Learning Toolbox
  • 硬件建议:配备NVIDIA GPU的电脑将显著提升检测速度(需安装对应CUDA驱动)
  • 额外组件:Computer Vision Toolbox, Parallel Computing Toolbox(可选但推荐)

提示:可通过MATLAB命令窗口输入ver命令查看已安装的工具箱列表,缺失组件可通过"附加功能管理器"在线安装。

1.2 YOLOv5s模型获取与转换

YOLOv5提供了多种预训练模型尺寸(从nano到xlarge),我们选择平衡速度与精度的s版本:

% 下载官方预训练模型(PyTorch格式)
model_url = 'https://github.com/ultralytics/yolov5/releases/download/v6.1/yolov5s.pt';
websave('yolov5s.pt', model_url);

% 转换为ONNX格式(需Python环境)
system('python export.py --weights yolov5s.pt --include onnx --img 640');

转换完成后,会得到yolov5s.onnx文件,这就是我们将在MATLAB中使用的模型文件。关键参数说明:

参数 说明
--weights yolov5s.pt 指定输入模型路径
--include onnx 指定输出格式为ONNX
--img 640 设置输入图像尺寸

1.3 ONNX模型导入MATLAB

MATLAB从R2017b开始支持ONNX模型导入,我们使用专门函数处理YOLOv5的特殊结构:

function importYOLOv5ONNX(modelPath)
    % 导入ONNX模型并生成对应MATLAB函数
    exportONNXFunction(modelPath, 'networks_yolov5sfcn');
    
    % 验证模型导入成功
    try
        net = importONNXFunction(modelPath, 'networks_yolov5sfcn');
        disp('模型导入成功!');
    catch ME
        error('模型导入失败: %s', ME.message);
    end
end

这个步骤会生成两个重要文件:

  • networks_yolov5sfcn.m:模型推理函数
  • yolov5s.mat:模型权重数据

2. 核心检测器类设计与实现

2.1 类属性定义

我们创建一个Detector_YOLOv5类来封装所有检测逻辑,首先定义关键属性:

classdef Detector_YOLOv5 < handle
    properties
        % 模型相关
        weights = [];       % 模型权重
        input_size = [640 640]; % 输入尺寸
        
        % 检测参数
        confThreshold = 0.25;  % 置信度阈值
        nmsThreshold = 0.45;   % NMS阈值
        
        % 类别信息
        cocoNames = {...};    % 80类英文名称
        cocoNames_CN = {...}; % 对应中文名称
        colors = [];          % 每个类别的显示颜色
        
        % 状态标志
        useGPU = false;      % 是否启用GPU加速
    end
end

注意:完整的80类中英文名称列表因篇幅限制在此省略,实际实现时需要完整包含COCO数据集的全部类别。

2.2 构造函数与模型初始化

类的构造函数负责模型加载和初始化工作:

methods
    function obj = Detector_YOLOv5(modelPath, useGPU)
        % 构造函数
        if nargin < 2
            useGPU = false;
        end
        
        % 初始化属性
        obj.useGPU = useGPU && canUseGPU();
        obj.colors = randi(255, length(obj.cocoNames_CN), 3);
        
        % 加载模型
        try
            obj.weights = importONNXFunction(modelPath, 'networks_yolov5sfcn');
            disp('模型加载成功!');
        catch ME
            error('模型加载失败: %s', ME.message);
        end
    end
end

2.3 核心检测方法实现

detect方法是整个类的核心,完成从图像输入到结果输出的完整流程:

function [bboxes, scores, labels] = detect(obj, image)
    % 输入校验
    if isempty(image)
        error('输入图像不能为空');
    end
    
    % 图像预处理
    [H,W,~] = size(image);
    img_resized = imresize(image, obj.input_size);
    img_normalized = rescale(img_resized, 0, 1);
    img_permuted = permute(img_normalized, [3,1,2]);
    dlarray_input = dlarray(reshape(img_permuted, [1,size(img_permuted)]));
    
    % GPU加速
    if obj.useGPU
        dlarray_input = gpuArray(dlarray_input);
    end
    
    % 模型推理
    [labels, bboxes] = networks_yolov5sfcn(...
        dlarray_input, obj.weights, ...
        'Training', false, ...
        'InputDataPermutation', 'none', ...
        'OutputDataPermutation', 'none');
    
    % 后处理
    if obj.useGPU
        labels = gather(extractdata(labels));
        bboxes = gather(extractdata(bboxes));
    end
    
    % 置信度过滤
    [maxScores, classIds] = max(labels, [], 2);
    validIdx = maxScores > obj.confThreshold;
    
    % 坐标转换
    predBoxes = bboxes(validIdx, :);
    predScores = maxScores(validIdx);
    predClasses = obj.cocoNames_CN(classIds(validIdx));
    
    % 转换为[x,y,w,h]格式
    boxesXYWH = [predBoxes(:,1)*W - predBoxes(:,3)*W/2, ...
                 predBoxes(:,2)*H - predBoxes(:,4)*H/2, ...
                 predBoxes(:,3)*W, ...
                 predBoxes(:,4)*H];
    
    % NMS处理
    [bboxes, scores, labels] = selectStrongestBboxMulticlass(...
        boxesXYWH, predScores, predClasses, ...
        'RatioType', 'Min', ...
        'OverlapThreshold', obj.nmsThreshold);
end

3. App Designer界面开发实战

3.1 界面布局设计

使用MATLAB App Designer创建名为ObjectDetectorApp的应用,主要包含以下组件:

  • 图像显示区域uiaxes组件,用于显示原始图像和检测结果
  • 控制面板
    • 检测源选择按钮组(图片/视频/摄像头)
    • 模型参数调节滑块(置信度、NMS阈值)
    • 结果显示表格
    • 操作按钮(开始/停止/保存)

关键布局参数:

% 主界面设置
app.UIFigure.AutoResizeChildren = 'off';
app.UIFigure.Position = [100 100 1200 800];
app.UIFigure.Name = 'YOLOv5目标检测系统';

% 图像显示区域
app.ImageAxes = uiaxes(app.UIFigure);
app.ImageAxes.Position = [20 20 800 760];

% 控制面板
app.ControlPanel = uipanel(app.UIFigure);
app.ControlPanel.Position = [840 20 340 760];

3.2 核心回调函数实现

3.2.1 图片检测功能
function DetectImageButtonPushed(app, event)
    % 打开文件选择对话框
    [file, path] = uigetfile({'*.jpg;*.png;*.bmp', '图像文件'});
    if isequal(file, 0)
        return; % 用户取消选择
    end
    
    % 读取并显示图像
    app.OriginalImage = imread(fullfile(path, file));
    imshow(app.OriginalImage, 'Parent', app.ImageAxes);
    
    % 执行检测
    tic;
    [bboxes, scores, labels] = app.Detector.detect(app.OriginalImage);
    elapsedTime = toc;
    
    % 显示结果
    app.displayDetectionResults(bboxes, scores, labels);
    app.updateStatusBar(sprintf('检测完成!耗时 %.2f 秒', elapsedTime));
end
3.2.2 视频检测功能
function DetectVideoButtonPushed(app, event)
    % 打开视频文件
    [file, path] = uigetfile({'*.mp4;*.avi', '视频文件'});
    if isequal(file, 0)
        return;
    end
    
    % 创建视频读取器
    app.VideoReader = VideoReader(fullfile(path, file));
    app.IsDetecting = true;
    
    % 启动定时器进行逐帧处理
    app.VideoTimer = timer(...
        'ExecutionMode', 'fixedRate', ...
        'Period', 0.05, ... % ~20fps
        'TimerFcn', @(~,~)app.processVideoFrame());
    
    start(app.VideoTimer);
end

function processVideoFrame(app)
    if ~app.IsDetecting || ~hasFrame(app.VideoReader)
        stop(app.VideoTimer);
        return;
    end
    
    % 读取并处理帧
    frame = readFrame(app.VideoReader);
    [bboxes, scores, labels] = app.Detector.detect(frame);
    
    % 显示结果
    annotatedFrame = app.annotateFrame(frame, bboxes, scores, labels);
    imshow(annotatedFrame, 'Parent', app.ImageAxes);
    drawnow;
end

3.3 结果可视化优化

为了让检测结果更加直观,我们实现专业的标注绘制方法:

function annotatedImage = annotateFrame(app, image, bboxes, scores, labels)
    % 创建副本
    annotatedImage = image;
    
    % 为每个检测结果绘制框和标签
    for i = 1:size(bboxes, 1)
        box = bboxes(i,:);
        label = labels{i};
        score = scores(i);
        color = app.Detector.colors(find(strcmp(app.Detector.cocoNames_CN, label)), :);
        
        % 绘制边界框
        annotatedImage = insertShape(annotatedImage, ...
            'Rectangle', box, ...
            'LineWidth', 3, ...
            'Color', color);
        
        % 添加标签文本
        textLabel = sprintf('%s: %.1f%%', label, score*100);
        annotatedImage = insertText(annotatedImage, ...
            [box(1), box(2)-20], ...
            textLabel, ...
            'FontSize', 14, ...
            'TextColor', 'white', ...
            'BoxColor', color, ...
            'BoxOpacity', 0.6);
    end
    
    % 添加帧率信息
    if isfield(app, 'LastFrameTime')
        fps = 1 / toc(app.LastFrameTime);
        annotatedImage = insertText(annotatedImage, ...
            [10 10], ...
            sprintf('FPS: %.1f', fps), ...
            'FontSize', 16, ...
            'TextColor', 'red', ...
            'BoxColor', 'black');
    end
    app.LastFrameTime = tic;
end

4. 高级功能扩展与性能优化

4.1 多模型切换支持

增强系统灵活性,允许用户根据需要切换不同版本的YOLOv5模型:

properties (Access = private)
    ModelList = {'yolov5s', 'yolov5m', 'yolov5l'};
    CurrentModel = 'yolov5s';
end

function ModelSelectionChanged(app, event)
    selectedModel = app.ModelDropDown.Value;
    if ~strcmp(selectedModel, app.CurrentModel)
        % 更新模型
        modelPath = fullfile('models', [selectedModel '.onnx']);
        app.Detector = Detector_YOLOv5(modelPath, app.UseGPUCheckBox.Value);
        app.CurrentModel = selectedModel;
        app.updateStatusBar(['已切换至模型: ' selectedModel]);
    end
end

4.2 实时摄像头检测优化

针对实时检测场景的特殊优化:

function CameraDetectionButtonPushed(app, event)
    if ~isempty(app.CameraObj) && isvalid(app.CameraObj)
        % 已经运行则停止
        stop(app.CameraObj);
        delete(app.CameraObj);
        app.CameraObj = [];
        app.CameraButton.Text = '启动摄像头';
        return;
    end
    
    % 初始化摄像头
    try
        app.CameraObj = webcam;
        app.IsDetecting = true;
        app.CameraButton.Text = '停止摄像头';
        
        % 创建处理定时器
        app.CameraTimer = timer(...
            'ExecutionMode', 'fixedRate', ...
            'Period', 0.05, ...
            'TimerFcn', @(~,~)app.processCameraFrame());
        
        start(app.CameraTimer);
    catch ME
        errordlg(sprintf('摄像头初始化失败: %s', ME.message));
    end
end

function processCameraFrame(app)
    if ~app.IsDetecting
        stop(app.CameraTimer);
        return;
    end
    
    % 获取并处理帧
    frame = snapshot(app.CameraObj);
    [bboxes, scores, labels] = app.Detector.detect(frame);
    
    % 显示结果
    annotatedFrame = app.annotateFrame(frame, bboxes, scores, labels);
    imshow(annotatedFrame, 'Parent', app.ImageAxes);
    drawnow;
end

4.3 性能优化技巧

通过以下方法可以显著提升系统响应速度:

  1. 图像尺寸优化

    % 根据实际需求调整输入尺寸
    app.Detector.input_size = [480 480]; % 较小尺寸提升速度但降低精度
    
  2. 异步处理机制

    % 使用parfeval进行后台检测
    future = parfeval(@app.Detector.detect, 3, image);
    % ...其他操作...
    [bboxes, scores, labels] = fetchOutputs(future);
    
  3. 检测区域限定

    % 只检测图像特定区域
    roi = [x y width height]; % 感兴趣区域
    croppedImage = imcrop(image, roi);
    results = detector.detect(croppedImage);
    % 将结果坐标转换回原图坐标系
    results.bboxes(:,1:2) = results.bboxes(:,1:2) + [roi(1) roi(2)];
    
  4. 模型量化加速

    % 将模型量化为INT8精度
    calibrator = dlquantizer(app.Detector.weights);
    calResults = calibrate(calibrator, calibrationData);
    quantizedNet = quantize(calibrator);
    

经过这些优化,即使在普通笔记本电脑上,系统也能达到接近实时的检测速度(>15FPS),满足大多数演示和实验需求。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐