【算法介紹】

基於YOLOv11的45種交通標誌檢測與識別系統,是專為智能交通與自動駕駛領域設計的深度學習解決方案。該系統利用YOLOv11目標檢測算法的最新優勢,實現對45類常見交通標誌的高效、精準識別,包括限速牌、停車標誌、禁止通行等。

系統核心採用YOLOv11模型,其網絡結構通過C3k2塊、SPPF模塊及C2PSA注意力機制的優化,顯著提升了特徵提取能力與空間注意力,尤其在複雜場景下對小目標和重疊目標的檢測精度顯著提升。在數據層面,系統基於TT100K等公開數據集進行訓練,結合旋轉、縮放、翻轉等數據增強技術,確保模型在多場景、多光照條件下的泛化能力。

系統架構包含三大模塊:圖像輸入、攝像頭實時檢測和視頻文件讀取;檢測與識別模塊通過YOLOv11實現交通標誌的快速定位與分類;結果輸出模塊在圖像上繪製檢測框並標註類別信息。

該系統可廣泛應用於車載輔助駕駛、交通監控及智能導航系統,通過提升道路標誌識別準確率,有效降低交通事故風險,推動智能交通技術的落地應用。

【效果展示】

[C#][winform]基於yolov11的45種交通標誌檢測與識別系統C#源碼+onnx模型+評估指標曲線+精美GUI界面_System

[C#][winform]基於yolov11的45種交通標誌檢測與識別系統C#源碼+onnx模型+評估指標曲線+精美GUI界面_System_02

【訓練數據集介紹】 數據集格式:YOLO格式(僅僅包含jpg圖片以及對應的yolo格式txt文件,已劃分好訓練驗證測試集) 圖片數量(jpg文件個數):9720 標註數量(xml文件個數):9720 標註數量(txt文件個數):9720 標註類別數:45 標註類別名稱(注意yolo格式類別順序不和這個對應,而以提供data.yaml類別順序為準):["i2","i2r","i4","i4l","i5","il60","il80","il100","ip","p3","p5","p6","p10","p11","p12","p13","p19","p23","p26","p27","pg","ph4","ph4.5","ph5","pl5","pl20","pl30","pl40","pl50","pl60","pl70","pl80","pl100","pl120","pm20","pm30","pm55","pn","pne","pr40","w13","w32","w55","w57","w59"] 每個類別標註的框數: i2 框數 = 471 i2r 框數 = 429 i4 框數 = 812 i4l 框數 = 335 i5 框數 = 1734 il60 框數 = 132 il80 框數 = 488 il100 框數 = 296 ip 框數 = 354 p3 框數 = 374 p5 框數 = 1580 p6 框數 = 189 p10 框數 = 379 p11 框數 = 128 p12 框數 = 296 p13 框數 = 839 p19 框數 = 134 p23 框數 = 173 p26 框數 = 421 p27 框數 = 116 pg 框數 = 157 ph4 框數 = 121 ph4.5 框數 = 186 ph5 框數 = 121 pl5 框數 = 670 pl20 框數 = 298 pl30 框數 = 164 pl40 框數 = 639 pl50 框數 = 1409 pl60 框數 = 537 pl70 框數 = 1071 pl80 框數 = 834 pl100 框數 = 148 pl120 框數 = 901 pm20 框數 = 156 pm30 框數 = 106 pm55 框數 = 139 pn 框數 = 3172 pne 框數 = 2383 pr40 框數 = 201 w13 框數 = 130 w32 框數 = 123 w55 框數 = 179 w57 框數 = 418 w59 框數 = 231 總框數:24174 使用標註工具:labelImg 標註規則:對類別進行畫矩形框 重要説明:暫無 特別聲明:本數據集不對訓練的模型或者權重文件精度作任何保證,數據集只提供準確且合理標注

圖片預覽:

[C#][winform]基於yolov11的45種交通標誌檢測與識別系統C#源碼+onnx模型+評估指標曲線+精美GUI界面_System_03

【訓練信息】

參數


訓練集圖片數

6793

驗證集圖片數

1931

訓練map

64.3%

訓練精度(Precision)

68.4%

訓練召回率(Recall)

58.5%

【驗證集精度統計】

Class

Images

Instances

P

R

mAP50

mAP50-95

all

1931

4797

0.684

0.585

0.643

0.459

i2

87

87

0.72

0.649

0.756

0.472

i2r

91

91

0.781

0.681

0.754

0.513

i4

154

164

0.833

0.67

0.788

0.493

i4l

73

74

0.82

0.676

0.774

0.537

i5

325

346

0.91

0.761

0.879

0.558

il60

69

91

0.819

0.894

0.919

0.7

il80

53

59

0.707

0.859

0.872

0.663

il100

28

28

0.831

0.703

0.88

0.664

ip

60

70

0.731

0.543

0.65

0.318

p3

30

30

0.82

0.6

0.697

0.522

p5

76

79

0.765

0.646

0.703

0.523

p6

23

23

0.563

0.392

0.513

0.447

p10

69

71

0.579

0.577

0.574

0.44

p11

313

315

0.749

0.622

0.713

0.459

p12

36

36

0.489

0.389

0.331

0.255

p13

52

82

0.664

0.598

0.634

0.379

p19

24

24

0.539

0.487

0.508

0.409

p23

60

61

0.734

0.574

0.668

0.493

p26

163

172

0.685

0.616

0.692

0.499

p27

29

29

0.425

0.379

0.368

0.282

pg

29

29

0.954

0.621

0.756

0.523

ph4

22

23

0.67

0.529

0.584

0.454

ph4.5

31

35

0.694

0.713

0.693

0.542

ph5

21

24

0.439

0.208

0.294

0.198

pl5

75

110

0.657

0.473

0.533

0.336

pl20

31

33

0.598

0.0904

0.202

0.138

pl30

122

124

0.606

0.5

0.55

0.413

pl40

277

286

0.69

0.476

0.611

0.421

pl50

209

210

0.579

0.471

0.527

0.377

pl60

170

174

0.526

0.575

0.564

0.441

pl70

28

28

0.696

0.573

0.597

0.478

pl80

158

165

0.713

0.661

0.713

0.52

pl100

76

131

0.813

0.797

0.883

0.689

pl120

47

59

0.679

0.79

0.781

0.607

pm20

26

26

0.629

0.538

0.66

0.498

pm30

17

17

0.251

0.412

0.252

0.217

pm55

31

32

0.662

0.675

0.72

0.583

pn

603

633

0.901

0.736

0.829

0.526

pne

429

471

0.92

0.779

0.876

0.537

pr40

37

37

0.82

0.892

0.945

0.785

w13

27

27

0.455

0.333

0.354

0.242

w32

23

23

0.678

0.261

0.349

0.175

w55

43

43

0.531

0.512

0.513

0.364

w57

77

82

0.648

0.718

0.737

0.514

w59

43

43

0.803

0.663

0.742

0.455

【測試環境】

windows10 x64系統 VS2019 netframework4.7.2 opencvsharp4.9.0 onnxruntime1.22.0

注意使用CPU推理,沒有使用cuda推理因此不需要電腦具有nvidia顯卡,無需安裝安裝cuda+dunn

【界面設計】

using DeploySharp.Data;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace FIRC
{
    public partial class Form1 : Form
    {
 
        public bool videoStart = false;//視頻停止標誌
        string weightsPath = Application.StartupPath + "\\weights";//模型目錄
        YoloDetector detetor = new YoloDetector();//推理引擎
        public Form1()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;//線程更新控件不報錯
        }
        private void LoadWeightsFromDir()
        {
            var di = new DirectoryInfo(weightsPath);
            foreach(var fi in di.GetFiles("*.onnx"))
            {
                comboBox1.Items.Add(fi.Name);
            }
            if(comboBox1.Items.Count>0)
            {
                comboBox1.SelectedIndex = 0;
            }
            else
            {
                tssl_show.Text = "未找到模型,請關閉程序,放入模型到weights文件夾!";
                tsb_pic.Enabled = false;
                tsb_video.Enabled = false;
                tsb_camera.Enabled = false;
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            LoadWeightsFromDir();//從目錄加載模型
                               
        }
        public string GetResultString(DetResult[] result)
        {
            Dictionary<string, int> resultDict = new Dictionary<string, int>();
            for (int i = 0; i < result.Length; i++)
            {
                if(resultDict.ContainsKey( result[i].Category) )
                {
                    resultDict[result[i].Category]++;
                }
                else
                {
                    resultDict[result[i].Category] =1;
                }
            }
 
            var resultStr = "";
            foreach(var item in resultDict)
            {
                resultStr += string.Format("{0}:{1}\r\n",item.Key,item.Value);
            }
            return resultStr;
        }
        private void tsb_pic_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
            if (ofd.ShowDialog() != DialogResult.OK) return;
            tssl_show.Text = "正在檢測中...";
            Task.Run(() => {
                var sw = new Stopwatch();
                sw.Start();
                Mat image = Cv2.ImRead(ofd.FileName);
                detetor.SetParams(Convert.ToSingle(numericUpDown1.Value), Convert.ToSingle(numericUpDown2.Value));
                var results=detetor.Inference(image);
                
                var resultImage = detetor.DrawImage(image, results);
    
                sw.Stop();
                pb_show.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(resultImage);
                tb_res.Text = GetResultString(results);
                tssl_show.Text = "檢測已完成!總計耗時"+sw.Elapsed.TotalSeconds+"秒";
            });
           
 
 
        }
 
        public void VideoProcess(string videoPath)
        {
            Task.Run(() => {
 
                detetor.SetParams(Convert.ToSingle(numericUpDown1.Value), Convert.ToSingle(numericUpDown2.Value));
                VideoCapture capture = new VideoCapture(videoPath);
                if (!capture.IsOpened())
                {
                    tssl_show.Text="視頻打開失敗!";
                    return;
                }
                Mat frame = new Mat();
                var sw = new Stopwatch();
                int fps = 0;
                while (videoStart)
                {
 
                    capture.Read(frame);
                    if (frame.Empty())
                    {
                        Console.WriteLine("data is empty!");
                        break;
                    }
                    sw.Start();
                    var results = detetor.Inference(frame);
                    var resultImg = detetor.DrawImage(frame,results);
                    sw.Stop();
                    fps = Convert.ToInt32(1 / sw.Elapsed.TotalSeconds);
                    sw.Reset();
                    Cv2.PutText(resultImg, "FPS=" + fps, new OpenCvSharp.Point(30, 30), HersheyFonts.HersheyComplex, 1.0, new Scalar(255, 0, 0), 3);
                    //顯示結果
                    pb_show.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(resultImg);
                    tb_res.Text = GetResultString(results);
                    Thread.Sleep(5);
 
 
                }
 
                capture.Release();
 
                pb_show.Image = null;
                tssl_show.Text = "視頻已停止!";
                tsb_video.Text = "選擇視頻";
 
            });
        }
        public void CameraProcess(int cameraIndex=0)
        {
            Task.Run(() => {
 
                detetor.SetParams(Convert.ToSingle(numericUpDown1.Value), Convert.ToSingle(numericUpDown2.Value));
                VideoCapture capture = new VideoCapture(cameraIndex);
                if (!capture.IsOpened())
                {
                    tssl_show.Text = "攝像頭打開失敗!";
                    return;
                }
                Mat frame = new Mat();
                var sw = new Stopwatch();
                int fps = 0;
                while (videoStart)
                {
 
                    capture.Read(frame);
                    if (frame.Empty())
                    {
                        Console.WriteLine("data is empty!");
                        break;
                    }
                    sw.Start();
                    var results = detetor.Inference(frame);
                    var resultImg = detetor.DrawImage(frame, results);
                    sw.Stop();
                    fps = Convert.ToInt32(1 / sw.Elapsed.TotalSeconds);
                    sw.Reset();
                    Cv2.PutText(resultImg, "FPS=" + fps, new OpenCvSharp.Point(30, 30), HersheyFonts.HersheyComplex, 1.0, new Scalar(255, 0, 0), 3);
                    //顯示結果
                    pb_show.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(resultImg);
                    tb_res.Text = GetResultString(results);
                    Thread.Sleep(5);
 
 
                }
 
                capture.Release();
 
                pb_show.Image = null;
                tssl_show.Text = "攝像頭已停止!";
                tsb_camera.Text = "打開攝像頭";
 
            });
        }
        private void tsb_video_Click(object sender, EventArgs e)
        {
            if(tsb_video.Text=="選擇視頻")
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "視頻文件(*.*)|*.mp4;*.avi";
                if (ofd.ShowDialog() != DialogResult.OK) return;
                videoStart = true;
                VideoProcess(ofd.FileName);
                tsb_video.Text = "停止";
                tssl_show.Text = "視頻正在檢測中...";
 
            }
            else
            {
                videoStart = false;
               
            }
        }
 
        private void tsb_camera_Click(object sender, EventArgs e)
        {
            if (tsb_camera.Text == "打開攝像頭")
            {
                videoStart = true;
                CameraProcess(0);
                tsb_camera.Text = "停止";
                tssl_show.Text = "攝像頭正在檢測中...";
 
            }
            else
            {
                videoStart = false;
 
            }
        }
 
        private void tsb_exit_Click(object sender, EventArgs e)
        {
            videoStart = false;
            this.Close();
        }
 
        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            numericUpDown1.Value = Convert.ToDecimal(trackBar1.Value / 100.0f);
        }
 
        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            numericUpDown2.Value = Convert.ToDecimal(trackBar2.Value / 100.0f);
        }
 
        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            trackBar1.Value = (int)(Convert.ToSingle(numericUpDown1.Value) * 100);
        }
 
        private void numericUpDown2_ValueChanged(object sender, EventArgs e)
        {
            trackBar2.Value = (int)(Convert.ToSingle(numericUpDown2.Value) * 100);
        }
 
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            tssl_show.Text="加載模型:"+comboBox1.Text;
            detetor.LoadWeights(weightsPath+"\\"+comboBox1.Text);
            tssl_show.Text = "模型加載已完成!";
        }
    }
}

【常用評估參數介紹】

在目標檢測任務中,評估模型的性能是至關重要的。你提到的幾個術語是評估模型性能的常用指標。下面是對這些術語的詳細解釋:

  1. Class
  • 這通常指的是模型被設計用來檢測的目標類別。例如,一個模型可能被訓練來檢測車輛、行人或動物等不同類別的對象。
  1. Images
  • 表示驗證集中的圖片數量。驗證集是用來評估模型性能的數據集,與訓練集分開,以確保評估結果的公正性。
  1. Instances
  • 在所有圖片中目標對象的總數。這包括了所有類別對象的總和,例如,如果驗證集包含100張圖片,每張圖片平均有5個目標對象,則Instances為500。
  1. P(精確度Precision)
  • 精確度是模型預測為正樣本的實例中,真正為正樣本的比例。計算公式為:Precision = TP / (TP + FP),其中TP表示真正例(True Positives),FP表示假正例(False Positives)。
  1. R(召回率Recall)
  • 召回率是所有真正的正樣本中被模型正確預測為正樣本的比例。計算公式為:Recall = TP / (TP + FN),其中FN表示假負例(False Negatives)。
  1. mAP50
  • 表示在IoU(交併比)閾值為0.5時的平均精度(mean Average Precision)。IoU是衡量預測框和真實框重疊程度的指標。mAP是一個綜合指標,考慮了精確度和召回率,用於評估模型在不同召回率水平上的性能。在IoU=0.5時,如果預測框與真實框的重疊程度達到或超過50%,則認為該預測是正確的。
  1. mAP50-95
  • 表示在IoU從0.5到0.95(間隔0.05)的範圍內,模型的平均精度。這是一個更嚴格的評估標準,要求預測框與真實框的重疊程度更高。在目標檢測任務中,更高的IoU閾值意味着模型需要更準確地定位目標對象。mAP50-95的計算考慮了從寬鬆到嚴格的多個IoU閾值,因此能夠更全面地評估模型的性能。

這些指標共同構成了評估目標檢測模型性能的重要框架。通過比較不同模型在這些指標上的表現,可以判斷哪個模型在實際應用中可能更有效。

【使用步驟】

使用步驟: (1)首先根據官方框架ultralytics安裝教程安裝好yolov11環境,並根據官方export命令將自己pt模型轉成onnx模型,然後去github倉庫futureflsl/firc-csharp-projects找到源碼 (2)使用vs2019打開sln項目,選擇x64 release並且修改一些必要的參數,比如輸入shape等,點擊運行即可查看最後效果

特別注意如果運行報錯了,請參考我的博文進行重新引用我源碼的DLL:[C#]opencvsharp報錯System.Memory,Version=4.0.1.2,Culture=neutral,PublicKeyToken=cc7b13fcd2ddd51“版本高於所引_未能加載文件或程序集“system.memory, version=4.0.1.2, culture-CSDN博客

【提供文件】

C#源碼

yolo11n.onnx模型(提供pytorch模型)

訓練的map,P,R曲線圖(在weights\results.png)

測試圖片(在test_img文件夾下面)

特別注意這裏提供訓練數據集