找回密码
 立即注册
首页 业界区 安全 智能光电检测:YOLO+OpenCV联合算法工程实践 ...

智能光电检测:YOLO+OpenCV联合算法工程实践

后仲舒 9 小时前
前言 最近,我独立开发了一套面向光电设备的上位机系统,用于实时拍摄与目标跟踪。起初,我对 AI 可谓一窍不通,但项目需求逼人,只能“硬核”补课。几番调研后,我锁定了当下最火的目标检测框架——YOLO。然而,YOLO 在大目标上表现惊艳,面对小目标却力不从心。为此,我又引入 OpenCV 的轮廓识别算法,将两种方案融合:大目标交给 YOLO,小目标交给 OpenCV,互补短板,整体精度显著提升。为了把算法落地,我啃完了 YOLO 标注、训练、推理的整条链路,并自研了一套配套软件,集成两大核心功能:

  • 智能标注:
    针对工业场景反复打磨,支持一键框选、快捷键批量操作、自动保存与回滚;相比市面工具,真正做到了“打开就会用,十分钟出活”。
  • 目标检测:
    图片或视频直接拖拽进软件,即可实时完成目标识别与跟踪,结果可视化、可导出,全流程无缝衔接。
本文将完整记录从零搭建这套系统的思路、踩坑与优化细节,并公开软件的核心功能设计,希望能给同样奋战在上位机与 AI 结合一线的开发者一点参考。1 图片搜集与标注为训练“鸟 vs 无人机”二分类模型,每类需准备上千张样本。数据源三路并进:

  • 自采:无人机实拍+长焦拍鸟,确保场景真实;
  • 开源:COCO 中抽取含鸟/无人机的切片;
  • 补充:淘宝购买高清图包、CSDN 资源帖批量下载。
下载完成后,按类别归档:dataset/bird/ ,dataset/drone/ 目录就绪,双击启动自研标注工具,开始高效标注。
1.gif
2 环境搭建
 虚拟环境使用conda,ide使用PyCharm,官网下载ultralytics。具体配置参见文章《基于YOLOv11的无人机目标检测实战(Windows环境)》
3 图片整理与训练
  图片按照训练集、验证集、测试集分类,比例为8:1:1 ,实现分类的代码如下
  1.   1 # 将图片和标注数据按比例切分为 训练集和测试集
  2.   2 import shutil
  3.   3 import random
  4.   4 import os
  5.   5
  6.   6 # 原始路径
  7.   7 image_original_path = "data/images/"
  8.   8 label_original_path = "data/labels/"
  9.   9
  10. 10 cur_path = os.getcwd()
  11. 11 # 训练集路径
  12. 12 train_image_path = os.path.join(cur_path, "datasets/images/train/")
  13. 13 train_label_path = os.path.join(cur_path, "datasets/labels/train/")
  14. 14
  15. 15 # 验证集路径
  16. 16 val_image_path = os.path.join(cur_path, "datasets/images/val/")
  17. 17 val_label_path = os.path.join(cur_path, "datasets/labels/val/")
  18. 18
  19. 19 # 测试集路径
  20. 20 test_image_path = os.path.join(cur_path, "datasets/images/test/")
  21. 21 test_label_path = os.path.join(cur_path, "datasets/labels/test/")
  22. 22
  23. 23 # 训练集目录
  24. 24 list_train = os.path.join(cur_path, "datasets/train.txt")
  25. 25 list_val = os.path.join(cur_path, "datasets/val.txt")
  26. 26 list_test = os.path.join(cur_path, "datasets/test.txt")
  27. 27
  28. 28 train_percent = 0.8
  29. 29 val_percent = 0.1
  30. 30 test_percent = 0.1
  31. 31
  32. 32
  33. 33 def del_file(path):
  34. 34     for i in os.listdir(path):
  35. 35         file_data = path + "\" + i
  36. 36         os.remove(file_data)
  37. 37
  38. 38
  39. 39 def mkdir():
  40. 40     if not os.path.exists(train_image_path):
  41. 41         os.makedirs(train_image_path)
  42. 42     else:
  43. 43         del_file(train_image_path)
  44. 44     if not os.path.exists(train_label_path):
  45. 45         os.makedirs(train_label_path)
  46. 46     else:
  47. 47         del_file(train_label_path)
  48. 48
  49. 49     if not os.path.exists(val_image_path):
  50. 50         os.makedirs(val_image_path)
  51. 51     else:
  52. 52         del_file(val_image_path)
  53. 53     if not os.path.exists(val_label_path):
  54. 54         os.makedirs(val_label_path)
  55. 55     else:
  56. 56         del_file(val_label_path)
  57. 57
  58. 58     if not os.path.exists(test_image_path):
  59. 59         os.makedirs(test_image_path)
  60. 60     else:
  61. 61         del_file(test_image_path)
  62. 62     if not os.path.exists(test_label_path):
  63. 63         os.makedirs(test_label_path)
  64. 64     else:
  65. 65         del_file(test_label_path)
  66. 66
  67. 67
  68. 68 def clearfile():
  69. 69     if os.path.exists(list_train):
  70. 70         os.remove(list_train)
  71. 71     if os.path.exists(list_val):
  72. 72         os.remove(list_val)
  73. 73     if os.path.exists(list_test):
  74. 74         os.remove(list_test)
  75. 75
  76. 76
  77. 77 def main():
  78. 78     mkdir()
  79. 79     clearfile()
  80. 80
  81. 81     file_train = open(list_train, 'w')
  82. 82     file_val = open(list_val, 'w')
  83. 83     file_test = open(list_test, 'w')
  84. 84
  85. 85     total_txt = os.listdir(label_original_path)
  86. 86     num_txt = len(total_txt)
  87. 87     list_all_txt = range(num_txt)
  88. 88
  89. 89     num_train = int(num_txt * train_percent)
  90. 90     num_val = int(num_txt * val_percent)
  91. 91     num_test = num_txt - num_train - num_val
  92. 92
  93. 93     train = random.sample(list_all_txt, num_train)
  94. 94     # train从list_all_txt取出num_train个元素
  95. 95     # 所以list_all_txt列表只剩下了这些元素
  96. 96     val_test = [i for i in list_all_txt if not i in train]
  97. 97     # 再从val_test取出num_val个元素,val_test剩下的元素就是test
  98. 98     val = random.sample(val_test, num_val)
  99. 99
  100. 100     print("训练集数目:{}, 验证集数目:{}, 测试集数目:{}".format(len(train), len(val), len(val_test) - len(val)))
  101. 101     for i in list_all_txt:
  102. 102         name = total_txt[i][:-4]
  103. 103
  104. 104         srcImage = image_original_path + name + '.jpg'
  105. 105         if os.path.isfile(srcImage):
  106. 106             suffix = '.jpg'
  107. 107         else:
  108. 108             suffix = '.png'
  109. 109             srcImage = image_original_path + name + '.png'
  110. 110
  111. 111         srcLabel = label_original_path + name + ".txt"
  112. 112
  113. 113         if i in train:
  114. 114             dst_train_Image = train_image_path + name + suffix
  115. 115             dst_train_Label = train_label_path + name + '.txt'
  116. 116             shutil.copyfile(srcImage, dst_train_Image)
  117. 117             shutil.copyfile(srcLabel, dst_train_Label)
  118. 118             file_train.write(dst_train_Image + '\n')
  119. 119         elif i in val:
  120. 120             dst_val_Image = val_image_path + name + '.jpg'
  121. 121             dst_val_Label = val_label_path + name + '.txt'
  122. 122             shutil.copyfile(srcImage, dst_val_Image)
  123. 123             shutil.copyfile(srcLabel, dst_val_Label)
  124. 124             file_val.write(dst_val_Image + '\n')
  125. 125         else:
  126. 126             dst_test_Image = test_image_path + name + suffix
  127. 127             dst_test_Label = test_label_path + name + '.txt'
  128. 128             shutil.copyfile(srcImage, dst_test_Image)
  129. 129             shutil.copyfile(srcLabel, dst_test_Label)
  130. 130             file_test.write(dst_test_Image + '\n')
  131. 131
  132. 132     file_train.close()
  133. 133     file_val.close()
  134. 134     file_test.close()
  135. 135
  136. 136
  137. 137 if __name__ == "__main__":
  138. 138     main()
复制代码
训练代码如下
  1. 1 import warnings
  2. 2 warnings.filterwarnings('ignore')
  3. 3 from ultralytics import YOLO
  4. 4 if __name__ == '__main__':
  5. 5   model = YOLO('ultralytics/cfg/models/12/yolo12n.yaml')
  6. 6   model.load('yolo12n.pt')  #注释则不加载
  7. 7   results = model.train(
  8. 8     data='data.yaml',  #数据集配置文件的路径
  9. 9     epochs=180,  #训练轮次总数
  10. 10     batch=4,  #批量大小,即单次输入多少图片训练
  11. 11     imgsz=640,  #训练图像尺寸
  12. 12     workers=2,  #加载数据的工作线程数
  13. 13     device= 0,  #指定训练的计算设备,无nvidia显卡则改为 'cpu'
  14. 14     optimizer='SGD',  #训练使用优化器,可选 auto,SGD,Adam,AdamW 等
  15. 15     amp= True,  #True 或者 False, 解释为:自动混合精度(AMP) 训练
  16. 16     patience = 50,
  17. 17     cache=False,  # True 在内存中缓存数据集图像,服务器推荐开启
  18. 18     augment=True,
  19. 19     lr0 = 0.001,
  20. 20     lrf = 0.01,
  21. 21     cos_lr=True,
  22. 22     weight_decay=0.05
  23. 23 )
复制代码
训练结果,mAP精度达到9提上,分数非常高。
2.png

4 图片视频目标识别
3.gif

视频文件目标识别
4.gif

后记 本文详细介绍了基于YOLO与OpenCV的光电上位机目标检测系统的开发过程。针对YOLO框架在小目标检测上的局限性,创新性地结合OpenCV轮廓识别算法,提升了系统的检测精度和适应性。通过数月的技术研究与实践,完成了以下工作:

  • 技术研究:深入掌握YOLO模型的标注、训练及推理流程,并结合OpenCV优化小目标识别效果。
  • 软件开发:自主设计并实现了一套功能完善的软件系统,包括图像标注工具和目标检测模块,显著提升了易用性和效率。
  • 创新优化:针对实际需求优化标注功能,使软件操作更便捷,性能优于市面同类产品。
本系统的开发不仅验证了YOLO与OpenCV结合的技术可行性,也为类似场景下的目标检测任务提供了可借鉴的解决方案。未来,可进一步优化模型性能,拓展多目标跟踪、实时检测等高级功能,以满足更广泛的应用需求。
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册