当前位置: 首页 > news >正文

基于Pytorch和yolov8n手搓安全帽目标检测的全过程

一.背景

     还是之前的主题,使用开源软件为公司搭建安全管理平台,从视觉模型识别安全帽开始。主要参考学习了开源项目 https://github.com/jomarkow/Safety-Helmet-Detection,我是从运行、训练、标注倒过来学习的。由于工作原因,抽空学习了vscode的使用、python语法,了解了pytorch、yolo、ultralytics、cuda、cuda toolkit、cudnn、AI的分类、AI的相关专业名词等等。到这里,基本可以利用工程化的方式解决目标检测环境搭建、AI标注、训练、运行全过程了。

二.捋一捋思路

1.人类目前的AI本质是一堆数据喂出个没有智慧的算命师

      大量的数量,AI最大程度(如95%)的找到了满足了数据输入与结果的因果关系。你说不智能吧?你人不一定能有它预测的准。你说智能吧?其实受限于有限的输入数据,毕竟不能穷举所有输入数据,人类掌握的数据既有限,也不一定就是客观的。个人愚见,不喜勿喷。

2.图形目标检测就是划圈圈、多看看、试一试的过程

     划圈圈就是数据标注,在图片上面框一下。多看看就是让算法自己看图片,好比我们教小孩子指着自己反复说“爸爸”,而后小孩慢慢的学会了叫“爸爸”的过程。试一试,就是把训练的结果(AI模型或者算法模型)拿来运行,就像让小孩对着另外一个男人让他去称呼,他可能也叫“爸爸”。那么,就需要纠正告诉他只有自己才是叫“爸爸”,其他的男人应该叫“叔叔”。图形目标检测就是这么个过程,没有什么神秘的。当然,我们是站在前辈肩膀上的,那些算法在那个年代写出来,确实是值得敬佩的。

3.环境与工具说明

      windows11家庭版、 显卡NVIDIA GeForce GT 730(我没有用GPU训练,主要还是显卡太老了,版本兼容性问题把我弄哭了,CPU慢点~~就慢点吧)、vscode1.83.0 、conda 24.9.2、Python 3.12.7、pytorch2.5.0、yolo8(后面换最新的yolo11试试)

三.开始手搓

1.创建空的工程结构

      1)在非中文、空格目录中创建object-detection-hello文件夹

      2)vscode打开文件夹

      3)在vscode中创建目录及文件等

      下图中的文件夹,并不是必须的,但是推荐这样

     4)编写训练需要的参数文件train_config.yaml     

train: ../../datas/images
val: ../../datas/images#class count
nc: 1
# names: ['helmet']
names: ['helmet']
labels: ../../datas/labels

      5)下载yolov8n.pt到models目录中

        在我之前上传的也有,详见本文章关联的资源。也可以去安全帽开源项目GitHub - jomarkow/Safety-Helmet-Detection: YoloV8 model, trained for recognizing if construction workers are wearing their protection helmets in mandatory areas中去下载,在根目录就有。

2.图片中安全帽标注

     1)图片准备

     去把安全帽的开源下载下来,里面有图片。我只选择了0-999,共1千张图片,毕竟我是cpu,训练慢,1千张估计也能有个效果了。

      2)图片标注

        参考之前的文章在windows系统中使用labelimg对图片进行标注之工具安装及简单使用-CSDN博客

       3)标注数据处理

         我标注后的文件是xml,需要转为txt文件。内容分别是        

<annotation><folder>images</folder><filename>hard_hat_workers2.png</filename><path>D:\zsp\works\temp\20241119-zsp-helmet\Safety-Helmet-Detection-main\data\images\hard_hat_workers2.png</path><source><database>Unknown</database></source><size><width>416</width><height>415</height><depth>3</depth></size><segmented>0</segmented><object><name>helmet</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>295</xmin><ymin>219</ymin><xmax>326</xmax><ymax>249</ymax></bndbox></object><object><name>helmet</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>321</xmin><ymin>212</ymin><xmax>365</xmax><ymax>244</ymax></bndbox></object>
</annotation>
0 0.745192 0.565060 0.072115 0.060241
0 0.826923 0.549398 0.081731 0.072289

   有时候,默认就是文本文件的格式了。如果不是,创建converter.py直接转换:    

from xml.dom import minidom
import osclasses={"helmet":0}def convert_coordinates(size, box):dw = 1.0/size[0]dh = 1.0/size[1]x = (box[0]+box[1])/2.0y = (box[2]+box[3])/2.0w = box[1]-box[0]h = box[3]-box[2]x = x*dww = w*dwy = y*dhh = h*dhreturn (x,y,w,h)def converter(classes):old_labels_path = "datas/images/raw_data/"new_labels_path = "datas/images/raw_data/"current_path = os.getcwd()# 打印当前工作目录print("当前路径是:", current_path)for file_name in os.listdir(old_labels_path):if ".xml" in file_name:old_file = minidom.parse(f"{old_labels_path}/{file_name}")name_out = (file_name[:-4]+'.txt')with open(f"{new_labels_path}/{name_out}", "w") as new_file:itemlist = old_file.getElementsByTagName('object')size = old_file.getElementsByTagName('size')[0]width = int((size.getElementsByTagName('width')[0]).firstChild.data)height = int((size.getElementsByTagName('height')[0]).firstChild.data)for item in itemlist:# get class labelclass_name =  (item.getElementsByTagName('name')[0]).firstChild.dataif class_name in classes:label_str = str(classes[class_name])else:label_str = "-1"print (f"{class_name} not in function classes")# get bbox coordinatesxmin = ((item.getElementsByTagName('bndbox')[0]).getElementsByTagName('xmin')[0]).firstChild.dataymin = ((item.getElementsByTagName('bndbox')[0]).getElementsByTagName('ymin')[0]).firstChild.dataxmax = ((item.getElementsByTagName('bndbox')[0]).getElementsByTagName('xmax')[0]).firstChild.dataymax = ((item.getElementsByTagName('bndbox')[0]).getElementsByTagName('ymax')[0]).firstChild.datab = (float(xmin), float(xmax), float(ymin), float(ymax))bb = convert_coordinates((width,height), b)#print(bb)#new_file.write(f"{label_str} {' '.join([(f'{a}.6f') for a in bb])}\n")new_file.write(f"{label_str} {' '.join([(f'{a:.6f}') for a in bb])}\n")print (f"wrote {name_out}")def main():converter(classes)if __name__ == '__main__':main()

    4)偷懒直接用开源项目标注的labels

     当然,也可以偷懒,复制开源项目的labels中0-999的txt文件到我们的labels目录。但是它文件是多目标检测,我们只保留下我们的安全帽标注,也就是txt文件中0开始的行。所以,我在AI的帮助下写了这个utils/deleteOtherclass.py程序,文件内容如下:     

import osPROY_FOLDER = os.getcwd().replace("\\","/")INPUT_FOLDER =  f"{PROY_FOLDER}/datas/labels/"
files = os.listdir(INPUT_FOLDER)def process_file(file_path):# 存储处理后的行processed_lines = []try:with open(file_path, 'r') as file:for line in file:if len(line) > 0 and line[0] == '0':  # 检查行的第一个字符是否为 0processed_lines.append(line)except FileNotFoundError:print(f"文件 {file_path} 未找到")returntry:with open(file_path, 'w') as file:  # 以写入模式打开文件,会清空原文件file.writelines(processed_lines)except Exception as e:print(f"写入文件时出现错误: {e}")for file_name in files:file_path = INPUT_FOLDER + file_nameprint(file_path)process_file(file_path)

    执行这个程序后,txt文件中就只剩下0开始的了,也就是我们安全帽的标注了。

  比如,hard_hat_workers0.txt文件前后内容分别如下:     

0 0.914663 0.349760 0.112981 0.141827
0 0.051683 0.396635 0.084135 0.091346
0 0.634615 0.379808 0.052885 0.091346
0 0.748798 0.391827 0.055288 0.086538
0 0.305288 0.397837 0.052885 0.069712
0 0.216346 0.397837 0.048077 0.069712
1 0.174279 0.379808 0.050481 0.067308
1 0.801683 0.383413 0.055288 0.088942
1 0.443510 0.411058 0.045673 0.072115
1 0.555288 0.400240 0.043269 0.074519
1 0.500000 0.383413 0.038462 0.064904
0 0.252404 0.360577 0.033654 0.048077
1 0.399038 0.393029 0.043269 0.064904
0 0.914663 0.349760 0.112981 0.141827
0 0.051683 0.396635 0.084135 0.091346
0 0.634615 0.379808 0.052885 0.091346
0 0.748798 0.391827 0.055288 0.086538
0 0.305288 0.397837 0.052885 0.069712
0 0.216346 0.397837 0.048077 0.069712
0 0.252404 0.360577 0.033654 0.048077

3.编写训练的程序

     Ultralytics的配置文件,一般存放在C:\Users\Dell\AppData\Roaming\Ultralytics\settings.json中,路径中的Dell你要换成你的用户名哦。当然,这里只是知道就好了。看看它内部的内容如下:   

{"settings_version": "0.0.6","datasets_dir": "D:\\zsp\\works\\temp\\20241218-zsp-pinwei\\object-detection-hello","weights_dir": "weights","runs_dir": "runs","uuid": "09253350c3bd45fd265c2e8346acaaa599711c1c3ef91e7e78ceff31d4132a83","sync": true,"api_key": "","openai_api_key": "","clearml": true,"comet": true,"dvc": true,"hub": true,"mlflow": true,"neptune": true,"raytune": true,"tensorboard": true,"wandb": false,"vscode_msg": true
}

     从配置的内容看,我们可能需要修改的是datasets_dir,为了更优雅,我写了代码来修改。

     1)用程序去修改配置文件的代码scripts/ultralytics_init.py     

from ultralytics import settingsimport osdef update_ultralytics_settings(key, value):try:#settings.update(key, value)  # 假设存在 update 方法settings[key]=valueprint(f"Updated {key} to {value} in ultralytics settings.")except AttributeError:print(f"Failed to update {key}, the update method may not exist in the settings module.")def init():current_path = os.getcwd()print(current_path)# 调用函数,使用形参,参数值用引号括起来update_ultralytics_settings("datasets_dir",current_path)print(settings)

   2)建立训练的主程序scripts/train.py     

import  ultralytics_init as uinit
uinit.init()from ultralytics import YOLOimport os# Return a specific setting
# value = settings["runs_dir"]model = YOLO("models/yolov8n.pt")
model.train(data="config/train_config.yaml", epochs=10)
result = model.val()
path = model.export(format="onnx")

   代码我我觉得不解释了,一看就明白。

    3)配置文件config/train_config.yaml的设置

#训练的图片集合
train: ../../datas/images
#过程验证的图片集合
val: ../../datas/images#目标类型的数量
nc: 1
#label的英文名称
names: ['helmet']

4.执行训练

     右上角,点三角形运行。

  训练了10代,训练过程约1小时。日志如下:

PS D:\zsp\works\temp\20241218-zsp-pinwei\object-detection-hello> & C:/Users/Dell/.conda/envs/myenv/python.exe d:/zsp/works/temp/20241218-zsp-pinwei/object-detection-hello/scripts/train.py
D:\zsp\works\temp\20241218-zsp-pinwei\object-detection-hello
Updated datasets_dir to D:\zsp\works\temp\20241218-zsp-pinwei\object-detection-hello in ultralytics settings.
JSONDict("C:\Users\Dell\AppData\Roaming\Ultralytics\settings.json"):
{"settings_version": "0.0.6","datasets_dir": "D:\\zsp\\works\\temp\\20241218-zsp-pinwei\\object-detection-hello","weights_dir": "weights","runs_dir": "runs","uuid": "09253350c3bd45fd265c2e8346acaaa599711c1c3ef91e7e78ceff31d4132a83","sync": true,"api_key": "","openai_api_key": "","clearml": true,"comet": true,"dvc": true,"hub": true,"mlflow": true,"neptune": true,"raytune": true,"tensorboard": true,"wandb": false,"vscode_msg": true
}
New https://pypi.org/project/ultralytics/8.3.55 available 😃 Update with 'pip install -U ultralytics'
Ultralytics 8.3.49 🚀 Python-3.12.7 torch-2.5.0 CPU (12th Gen Intel Core(TM) i7-12700)
engine\trainer: task=detect, mode=train, model=models/yolov8n.pt, data=config/train_config.yaml, epochs=10, time=None, patience=100, batch=16, imgsz=640, save=True, save_period=-1, cache=False, device=None, workers=8, project=None, name=train, exist_ok=False, pretrained=True, optimizer=auto, verbose=True, seed=0, deterministic=True, single_cls=False, rect=False, cos_lr=False, close_mosaic=10, resume=False, amp=True, fraction=1.0, profile=False, freeze=None, multi_scale=False, overlap_mask=True, mask_ratio=4, dropout=0.0, val=True, split=val, save_json=False, save_hybrid=False, conf=None, iou=0.7, max_det=300, half=False, dnn=False, plots=True, source=None, vid_stride=1, stream_buffer=False, visualize=False, augment=False, agnostic_nms=False, classes=None, retina_masks=False, embed=None, show=False, save_frames=False, save_txt=False, save_conf=False, save_crop=False, show_labels=True, show_conf=True, show_boxes=True, line_width=None, format=torchscript, keras=False, optimize=False, int8=False, dynamic=False, simplify=True, opset=None, workspace=None, nms=False, lr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=7.5, cls=0.5, dfl=1.5, pose=12.0, kobj=1.0, nbs=64, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, bgr=0.0, mosaic=1.0, mixup=0.0, copy_paste=0.0, copy_paste_mode=flip, auto_augment=randaugment, erasing=0.4, crop_fraction=1.0, cfg=None, tracker=botsort.yaml, save_dir=runs\detect\train
Overriding model.yaml nc=80 with nc=1from  n    params  module                                       arguments     0                  -1  1       464  ultralytics.nn.modules.conv.Conv             [3, 16, 3, 2] 1                  -1  1      4672  ultralytics.nn.modules.conv.Conv             [16, 32, 3, 2]2                  -1  1      7360  ultralytics.nn.modules.block.C2f             [32, 32, 1, True]3                  -1  1     18560  ultralytics.nn.modules.conv.Conv             [32, 64, 3, 2]4                  -1  2     49664  ultralytics.nn.modules.block.C2f             [64, 64, 2, True]5                  -1  1     73984  ultralytics.nn.modules.conv.Conv             [64, 128, 3, 2]6                  -1  2    197632  ultralytics.nn.modules.block.C2f             [128, 128, 2, True]7                  -1  1    295424  ultralytics.nn.modules.conv.Conv             [128, 256, 3, 2]8                  -1  1    460288  ultralytics.nn.modules.block.C2f             [256, 256, 1, True]9                  -1  1    164608  ultralytics.nn.modules.block.SPPF            [256, 256, 5] 10                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']11             [-1, 6]  1         0  ultralytics.nn.modules.conv.Concat           [1]           12                  -1  1    148224  ultralytics.nn.modules.block.C2f             [384, 128, 1] 13                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']14             [-1, 4]  1         0  ultralytics.nn.modules.conv.Concat           [1]           15                  -1  1     37248  ultralytics.nn.modules.block.C2f             [192, 64, 1]  16                  -1  1     36992  ultralytics.nn.modules.conv.Conv             [64, 64, 3, 2]17            [-1, 12]  1         0  ultralytics.nn.modules.conv.Concat           [1]           18                  -1  1    123648  ultralytics.nn.modules.block.C2f             [192, 128, 1] 19                  -1  1    147712  ultralytics.nn.modules.conv.Conv             [128, 128, 3, 2]20             [-1, 9]  1         0  ultralytics.nn.modules.conv.Concat           [1]           21                  -1  1    493056  ultralytics.nn.modules.block.C2f             [384, 256, 1] 22        [15, 18, 21]  1    751507  ultralytics.nn.modules.head.Detect           [1, [64, 128, 256]]
Model summary: 225 layers, 3,011,043 parameters, 3,011,027 gradients, 8.2 GFLOPstrain: Scanning D:\zsp\works\temp\20241218-zsp-pinwei\object-detection-hello\datas\labels.cache.
val: Scanning D:\zsp\works\temp\20241218-zsp-pinwei\object-detection-hello\datas\labels.cache...
Plotting labels to runs\detect\train\labels.jpg...
optimizer: 'optimizer=auto' found, ignoring 'lr0=0.01' and 'momentum=0.937' and determining best 'optimizer', 'lr0' and 'momentum' automatically...
optimizer: AdamW(lr=0.002, momentum=0.9) with parameter groups 57 weight(decay=0.0), 64 weight(decay=0.0005), 63 bias(decay=0.0)
Image sizes 640 train, 640 val
Using 0 dataloader workers
Logging results to runs\detect\train
Starting training for 10 epochs...
Closing dataloader mosaicEpoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size1/10         0G      1.592      2.148      1.278         40        640: 100%|██████████| Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [02:58<00:00,  5.57s/it]all       1000       3792      0.977      0.033      0.423      0.229Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size2/10         0G      1.483      1.464      1.215         23        640: 100%|██████████| 63/63 [07:53<00:00,  7.51s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:20<00:00,  2.51s/it]all       1000       3792      0.697      0.647      0.687      0.398Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size3/10         0G      1.489      1.318      1.242         15        640: 100%|██████████| 63/63 [02:52<00:00,  2.74s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:13<00:00,  2.30s/it]all       1000       3792      0.783      0.662      0.744      0.401Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size4/10         0G       1.47      1.183       1.22         19        640: 100%|██████████| 63/63 [02:50<00:00,  2.71s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:12<00:00,  2.25s/it]all       1000       3792      0.837      0.749      0.832      0.496Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size5/10         0G       1.42      1.041      1.196         31        640: 100%|██████████| 63/63 [02:51<00:00,  2.72s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:11<00:00,  2.22s/it]all       1000       3792      0.867      0.776       0.87      0.537Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size6/10         0G        1.4     0.9758      1.196         30        640: 100%|██████████| 63/63 [02:51<00:00,  2.72s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:11<00:00,  2.24s/it]all       1000       3792      0.898      0.818      0.902      0.565Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size7/10         0G      1.352     0.8787      1.156         37        640: 100%|██████████| 63/63 [02:52<00:00,  2.74s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:20<00:00,  2.51s/it]all       1000       3792      0.921      0.843      0.922      0.576Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size8/10         0G      1.307      0.825       1.13         17        640: 100%|██████████| 63/63 [06:18<00:00,  6.01s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:11<00:00,  2.22s/it]all       1000       3792      0.906      0.845      0.924       0.58Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size9/10         0G      1.294     0.7867      1.133         29        640: 100%|██████████| 63/63 [02:51<00:00,  2.72s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:10<00:00,  2.21s/it]all       1000       3792      0.922       0.87      0.938      0.611Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size10/10         0G      1.257     0.7387      1.119         57        640: 100%|██████████| 63/63 [02:51<00:00,  2.72s/it]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [01:18<00:00,  2.47s/it]all       1000       3792      0.933      0.884       0.95       0.6210 epochs completed in 0.934 hours.
Optimizer stripped from runs\detect\train\weights\last.pt, 6.2MB
Optimizer stripped from runs\detect\train\weights\best.pt, 6.2MBValidating runs\detect\train\weights\best.pt...
Ultralytics 8.3.49 🚀 Python-3.12.7 torch-2.5.0 CPU (12th Gen Intel Core(TM) i7-12700)
Model summary (fused): 168 layers, 3,005,843 parameters, 0 gradients, 8.1 GFLOPsClass     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 32/32 [00:59<00:00,  1.86s/it]all       1000       3792      0.933      0.884       0.95       0.62
Speed: 1.4ms preprocess, 51.6ms inference, 0.0ms loss, 0.4ms postprocess per image
Results saved to runs\detect\train
Ultralytics 8.3.49 🚀 Python-3.12.7 torch-2.5.0 CPU (12th Gen Intel Core(TM) i7-12700)
Model summary (fused): 168 layers, 3,005,843 parameters, 0 gradients, 8.1 GFLOPs
val: Scanning D:\zsp\works\temp\20241218-zsp-pinwei\object-detection-hello\datas\labels.cache... 1000 images, 76 backgrounds, 0 corrupt: 100%|██████████| 1000/1000 [00:00<?, ?it/s]Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 63/63 [00:54<00:00,  1.17it/s]all       1000       3792      0.933      0.884       0.95       0.62
Speed: 1.1ms preprocess, 46.3ms inference, 0.0ms loss, 0.4ms postprocess per image
Results saved to runs\detect\train2
Ultralytics 8.3.49 🚀 Python-3.12.7 torch-2.5.0 CPU (12th Gen Intel Core(TM) i7-12700)PyTorch: starting from 'runs\detect\train\weights\best.pt' with input shape (1, 3, 640, 640) BCHW and output shape(s) (1, 5, 8400) (6.0 MB)ONNX: starting export with onnx 1.17.0 opset 19...
ONNX: slimming with onnxslim 0.1.43...
ONNX: export success ✅ 1.0s, saved as 'runs\detect\train\weights\best.onnx' (11.7 MB)Export complete (1.1s)
Results saved to D:\zsp\works\temp\20241218-zsp-pinwei\object-detection-hello\runs\detect\train\weights
Predict:         yolo predict task=detect model=runs\detect\train\weights\best.onnx imgsz=640
Validate:        yolo val task=detect model=runs\detect\train\weights\best.onnx imgsz=640 data=config/train_config.yaml
Visualize:       https://netron.app

5.运行训练后的模型看看效果

       1)把训练后的模型best.pt准备好

        训练结果模型在哪里?看看日志啊Results saved to D:\zsp\works\temp\20241218-zsp-pinwei\object-detection-hello\runs\detect\train\weights。我去把它复制到了test文件夹中。

    

      2)把测试图片1.jpg复制到test目录下      

    注意:图片中的马赛克是为了保护同事隐私添加的,并非程序效果。

     3)编写验证代码script/test.py

     代码也不解释了,一看就明白的

import os
from ultralytics import YOLO
import cv2PROY_FOLDER = os.getcwd().replace("\\","/")INPUT_FOLDER =  f"{PROY_FOLDER}/test/"
OUTPUT_FOLDER = f"{PROY_FOLDER}/test_out/"
MODEL_PATH =    f"{PROY_FOLDER}/test/best.pt"if not os.path.exists(OUTPUT_FOLDER):os.mkdir(OUTPUT_FOLDER)model = YOLO(MODEL_PATH) 
files = os.listdir(INPUT_FOLDER)def draw_box(params, frame, threshold = 0.2):x1, y1, x2, y2, score, class_id = paramsif score > threshold:cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 4)textValue=results.names[int(class_id)].upper()if "HELMET" in textValue :textValue="yes"cv2.putText(frame, textValue, (int(x1), int(y1 - 10)),cv2.FONT_HERSHEY_SIMPLEX, 1.3, (0, 255, 0), 3, cv2.LINE_AA)  elif "HEAD" in textValue :textValue="no!!!"cv2.putText(frame, textValue, (int(x1), int(y1 - 10)),cv2.FONT_HERSHEY_SIMPLEX, 1.3, (0, 255, 0), 3, cv2.LINE_AA)  else:                cv2.putText(frame, textValue, (int(x1), int(y1 - 10)),cv2.FONT_HERSHEY_SIMPLEX, 1.3, (0, 255, 0), 3, cv2.LINE_AA)  return framefor file_name in files:file_path = INPUT_FOLDER + file_nameif ".jpg" in file_name:image_path_out = OUTPUT_FOLDER + file_name[:-4] + "_out.jpg"image = cv2.imread(file_path,cv2.IMREAD_COLOR) results = model(image)[0]for result in results.boxes.data.tolist():image = draw_box(result, image)            cv2.imwrite(image_path_out, image)    cv2.destroyAllWindows()

      4)运行结果查看

    注意:图片中的马赛克是为了保护同事隐私添加的,并非程序效果。

四.总结

     到这里,我们就完成了从标注、写代码训练、验证训练结果的全过程。为我们后面搭建一个安全帽检测的服务奠定了基础,当然对于训练结果的调优干预还是我们的短板,毕竟是初学,我想未来都不是问题。我有编程基础,过程中还是出现了不少的问题,但只要努力尝试去看输出日志,都能解决。不行的话,把输出日志拿去问AI都能找到解决问题的思路。

相关文章:

基于Pytorch和yolov8n手搓安全帽目标检测的全过程

一.背景 还是之前的主题&#xff0c;使用开源软件为公司搭建安全管理平台&#xff0c;从视觉模型识别安全帽开始。主要参考学习了开源项目 https://github.com/jomarkow/Safety-Helmet-Detection&#xff0c;我是从运行、训练、标注倒过来学习的。由于工作原因&#xff0c;抽空…...

数据结构与算法基础(C语言版)

参考资料&#xff1a;https://www.bilibili.com/video/BV1GW421A7qY/ 所有代码均已在Ubuntu 20.04.6 LTS中测试通过 逻辑结构与存储结构 逻辑结构 逻辑结构指的是数据对象中数据元素间的相互关系&#xff0c;分为以下四种&#xff1a; 集合结构 集合结构中的数据元素除了同属于…...

Qt监控系统放大招/历经十几年迭代完善/多屏幕辅屏预览/多层级设备树/网络登录和回放

一、前言说明 近期对视频监控系统做了比较大的更新升级&#xff0c;主要就是三点&#xff0c;第一点就是增加了辅屏预览&#xff0c;这个也是好多个客户需要的功能&#xff0c;海康的iVMS-4200客户端就有这个功能&#xff0c;方便在多个屏幕打开不同的视频进行查看&#xff0c…...

使用Locust对MySQL进行负载测试

1.安装环境 pip install locust mysql-connector-python 2.设置测试环境 打开MySQL服务 打开Navicat新建查询&#xff0c;输入SQL语句 3.编写locust脚本 load_mysql.py # codingutf-8 from locust import User, TaskSet, task, between import mysql.connector import ran…...

layui多图上传,tp8后端接收处理

环境&#xff1a;layui2.9.21\thinkphp8.1 前端代码&#xff1a; layui.use([upload, layer], function() {const upload layui.upload;const layer layui.layer;const $ layui.$;// 上传图片const uploadInstImage upload.render({elem: #uploadImage,url: /admin/demo/…...

【Rust自学】8.3. String类型 Pt.1:字符串的创建、更新与拼接

8.3.0. 本章内容 第八章主要讲的是Rust中常见的集合。Rust中提供了很多集合类型的数据结构&#xff0c;这些集合可以包含很多值。但是第八章所讲的集合与数组和元组有所不同。 第八章中的集合是存储在堆内存上而非栈内存上的&#xff0c;这也意味着这些集合的数据大小无需在编…...

Linux(Ubuntu)下ESP-IDF下载与安装完整流程(1)

本文主要看参考官网说明,如下: 快速入门 - ESP32-S3 - — ESP-IDF 编程指南 latest 文档 Linux 和 macOS 平台工具链的标准设置 - ESP32-S3 - — ESP-IDF 编程指南 latest 文档 一、安装准备 为了在ESP32-S3中使用ESP-IDF,需要根据操作系统安装一些软件包。可以参考以下安…...

Android性能优化—— 内存优化

Android 系统中&#xff0c;垃圾回收是自动的&#xff0c;比较隐蔽&#xff0c;这就导致一些内存问题表现的并不明显&#xff0c;出现问题后难以定位。常见的内存问题有内存泄漏、内存溢出&#xff08;Out of Memory&#xff09;、内存抖动等。 我们做内存优化的主要原因有以下…...

【MATLAB第111期】基于MATLAB的sobol全局敏感性分析方法二阶指数计算

【MATLAB第111期】基于MATLAB的sobol全局敏感性分析方法二阶指数计算 一、简介 在MATLAB中计算Sobol二阶效应指数通常涉及到全局敏感性分析&#xff08;Global Sensitivity Analysis, GSA&#xff09;&#xff0c;其中Sobol方法是一种流行的技术&#xff0c;用于评估模型输入…...

【Scala】图书项目系统代码演练3.1/BookService

package org.app package serviceimport models.{BookModel, BorrowRecordModel}import org.app.dao.{BookDAO, BorrowRecordDAO}import java.time.LocalDateTime import scala.collection.mutable.ListBuffer// 图书业务逻辑层 class BookService {private val bookDAO new B…...

Gateway

目录 使用 工作机制 Predicate断言 Filter过滤器&#xff08;鉴权&#xff09; GatewayFilter GlobalFilter 使用 Gateway底层是使用Ribbon来实现负载均衡的 新建模块&#xff0c;端口5001 1、引入依赖 spring-boot-starter-web里存在tomcat&#xff0c;spring-cloud-st…...

SpringSecurity中的过滤器链与自定义过滤器

关于 Spring Security 框架中的过滤器的使用方法,系列文章: 《SpringSecurity中的过滤器链与自定义过滤器》 《SpringSecurity使用过滤器实现图形验证码》 1、Spring Security 中的过滤器链 Spring Security 中的过滤器链(Filter Chain)是一个核心的概念,它定义了一系列过…...

【OTA】论文学习笔记--《基于RTOS的车载ECU双分区OTA升级技术分析报告》

引言 研究背景 - 汽车智能化、网联化发展趋势下,OTA升级已成为智能网联汽车的必备功能 - 传统RTOS控制器在OTA升级失败后无法进行软件回滚,导致控制器功能失效 - 现有技术主要针对Linux、QNX等智能操作系统,缺乏针对RTOS的解决方案 研究目的 提出一种适用于RTOS控制器的双分…...

MLA:多头潜在注意力

MLA:多头潜在注意力 多头潜在注意力(MLA)机制是一种在深度学习模型中用于处理序列数据的注意力机制的改进形式,以下是对其原理和示例的详细介绍: 原理 低秩键值联合压缩:MLA机制利用低秩键值联合压缩来消除注意力模块中的某些计算,从而提高模型的运行速度和性能。在传…...

【python】matplotlib(moon cake)

文章目录 1、Style12、Style23、Style34、Style45、Style56、Style67、Style78、参考的库函数matplotlib.patches.Arcmatplotlib.patches.Wedge 9、参考 1、Style1 """ author: tyran """from numpy import sin, cos, pi import matplotlib.pyp…...

C++ hashtable

文章目录 1. 基本概念2. 哈希函数3. 哈希冲突及解决方法开放定址法链地址法再哈希法建立公共溢出区4. 哈希表的操作实现5. 内存管理及优化 时间复杂度理想情况&#xff08;无哈希冲突或冲突极少&#xff09;一般情况&#xff08;考虑哈希冲突及解决方法&#xff09;综合来看 以…...

ip怎么查域名?IP和域名分别是什么?

为什么我们可以通过简单的域名访问网站&#xff0c;而不是记住一串复杂的数字&#xff1f;IP地址和域名之间到底是什么关系&#xff1f;在互联网的世界里&#xff0c;IP地址和域名是两种重要的概念&#xff0c;它们共同构成了我们日常上网的基础。 IP地址是互联网协议地址的缩…...

大模型提示词初探

大模型提示词初探 在与大模型交互的过程中&#xff0c;提示词起着至关重要的作用&#xff0c;它犹如给模型下达的精准任务指令&#xff0c;直接影响着模型生成内容的准确性、高效性与合理性。合理运用提示词&#xff0c;能够有效减少模型出现错误和幻觉的情况&#xff0c;从而…...

Linux的进程替换以及基础IO

进程替换 上一篇草率的讲完了进程地址空间的组成结构和之间的关系&#xff0c;那么我们接下来了解一下程序的替换。 首先&#xff0c;在进程部分我们提过了&#xff0c;其实文件可以在运行时变成进程&#xff0c;而我们使用的Linux软件其实也是一个进程&#xff0c;所以进一步…...

《Vue进阶教程》第三十一课:ref的初步实现

往期内容&#xff1a; 《Vue进阶教程》第二十课&#xff1a;lazy懒执行 《Vue进阶教程》第二十一课&#xff1a;支持缓存 《Vue进阶教程》第二十二课&#xff1a;自定义更新(调度器) 《Vue进阶教程》第二十三课&#xff1a;渲染计算属性的结果 《Vue进阶教程》第二十四课&…...

Wend看源码-Java-集合学习(Queue)

概述 Wend看源码-Java-集合学习(List)-CSDN博客 Wend看源码-Java-集合学习(Set)-CSDN博客 在前两篇文章中&#xff0c;我们分别探讨了Java集合框架的父类以及List集合和Set集合的实现。接下来&#xff0c;本文将重点阐述Java中的Queue集合&#xff0c;包括其内部的数据结…...

大数据面试笔试宝典之HBase面试

1.HBase 数据存储位置 HBase 中的数据存储在哪里? 以什么格式存储?和 Zookeeper 什么关系? 参考答案: 1.HBase 中的所有数据文件都存储在 Hadoop HDFS 文件系统上。 2.包含两种文件类型: HFile : HBase 中 KeyValue 数据的存储格式。HFile 是 Hadoop 的二进制格式文件…...

React基础知识学习

学习React前端框架是一个系统而深入的过程&#xff0c;以下是一份详细的学习指南&#xff1a; 一、React基础知识 React简介 React是一个用于构建用户界面的JavaScript库&#xff0c;由Facebook开发和维护。它强调组件化和声明式编程&#xff0c;使得构建复杂的用户界面变得更…...

实战指南:Shiro、CAS打造完美单点登录体验

引言 想象一下&#xff0c;在日常工作中&#xff0c;我们经常需要进行系统认证和授权。当用户尝试登录一个网站时&#xff0c;他们需要提供用户名和密码&#xff0c;网站会检查这些信息&#xff0c;确认用户是谁。这就是认证的过程。 一旦用户被认证&#xff0c;他们可能会尝…...

光储充一体化解决方案详解。

一、光储充介绍 1、什么是光储充 “光储充”一体化&#xff0c;顾名思义&#xff0c;是由光伏发电、储能、充电集成一体、互相协调支撑的绿色充电模式。其工作原理是利用光伏发电&#xff0c;余电由储能设备存储&#xff0c;共同承担供电充电任务。在用电高峰&#xff0c;光储…...

TDengine 新功能 VARBINARY 数据类型

1. 背景 VARBINARY 数据类型用于存储二进制数据&#xff0c;与 MySQL 中的 VARBINARY 数据类型功能相同&#xff0c;VARBINARY 数据类型长度可变&#xff0c;在创建表时指定最大字节长度&#xff0c;使用进按需分配存储&#xff0c;但不能超过建表时指定的最大值。 2. 功能说明…...

2024年秋词法分析作业(满分25分)

【问题描述】 请根据给定的文法设计并实现词法分析程序&#xff0c;从源程序中识别出单词&#xff0c;记录其单词类别和单词值&#xff0c;输入输出及处理要求如下&#xff1a; &#xff08;1&#xff09;数据结构和与语法分析程序的接口请自行定义&#xff1b;类别码需按下表格…...

AF3 checkpoint_blocks函数解读

checkpoint_blocks 函数实现了一种分块梯度检查点机制 (checkpoint_blocks),目的是通过分块(chunking)执行神经网络模块,减少内存使用。在深度学习训练中,梯度检查点(activation checkpointing)是一种显存优化技术。该代码可以: 对神经网络的块(blocks)按需分块,并对…...

VITUREMEIG | AR眼镜 算力增程

根据IDC发布的《2024年第三季度美国AR/VR市场报告》显示&#xff0c;美国市场AR/VR总出货量增长10.3%。其中&#xff0c;成立于2021年的VITURE增长速度令人惊艳&#xff0c;同比暴涨452.6%&#xff0c;成为历史上增长最快的AR/VR品牌。并在美国AR领域占据了超过50%的市场份额&a…...

详解MySQL在Windows上的安装

目录 查看电脑上是否安装了MySQL 下载安装MySQL 打开MySQL官网&#xff0c;找到DOWNLOADS 然后往下翻&#xff0c;找到MySQL Community(GPL) Downloads>> 然后找到MySQL Community Server 然后下载&#xff0c;选择No thanks,just start my download. 然后双击进行…...

细说STM32F407单片机CAN基础知识及其HAL驱动程序

目录 一、CAN总线结构和传输协议 1、 CAN总线结构 &#xff08;1&#xff09;闭环结构的CAN总线网络 &#xff08;2&#xff09;开环结构的CAN总线网络 &#xff08;3&#xff09;隐性电平和显性电平 2、CAN总线传输协议 &#xff08;1&#xff09;CAN总线传输特点 &am…...

Ubuntu 22.04 升级 24.04 问题记录

一台闲置笔记本使用的 ubuntu 还是 18.04&#xff0c;最近重新使用&#xff0c;发现版本过低&#xff0c;决定升级&#xff0c;于是完成了 18.04 -> 20.04 -> 22. 04 -> 24.04 的三连跳。 一、升级过程中黑屏 主要问题是在 22.04 升级到 24.04 过程中出现了黑屏仅剩…...

模型选择+过拟合欠拟合

训练误差和泛化误差 训练误差&#xff1a;模型在训练数据上的误差 泛化误差&#xff1a;模型在新数据上的误差 验证数据集&#xff1a;一个用来评估模型好坏的数据集 例如拿出50%的数据作为训练 测试数据集&#xff1a;只能用一次 K则交叉验证 在没有足够数据时使用 算法…...

Leetcode 3404. Count Special Subsequences

Leetcode 3404. Count Special Subsequences 1. 解题思路2. 代码实现 题目链接&#xff1a;3404. Count Special Subsequences 1. 解题思路 这道题是事实上这次的周赛最难的一道题目&#xff0c;不过也是有点巧思在内。 最开始我的想法就是按照乘积构成pair&#xff0c;然后…...

萌萌哒的八戒

萌萌哒的八戒 下载压缩包后&#xff0c;打开发现有一张照片 既然是关于猪的&#xff0c;那就用猪圈密码解码 flag{whenthepigwanttoeat}...

开启家具组装新方式:产品说明书智能指导

在快节奏的现代生活中&#xff0c;人们越来越追求便捷与高效。无论是新房装修还是家具换新&#xff0c;家具组装已成为许多家庭不可避免的一项任务。然而&#xff0c;面对复杂多变的家具图纸和冗长的产品说明书&#xff0c;许多人常常感到无从下手&#xff0c;甚至因操作不当而…...

【连续学习之ResCL算法】2020年AAAI会议论文:Residual continual learning

1 介绍 年份&#xff1a;2020 会议&#xff1a; AAAI Lee J, Joo D, Hong H G, et al. Residual continual learning[C]//Proceedings of the AAAI Conference on Artificial Intelligence. 2020, 34(04): 4553-4560. 本文提出的算法是Residual Continual Learning (ResC…...

【网络协议】路由信息协议 (RIP)

未经许可&#xff0c;不得转载。 路由信息协议&#xff08;Routing Information Protocol&#xff0c;简称 RIP&#xff09;是一种使用跳数&#xff08;hop count&#xff09;作为路由度量标准的路由协议&#xff0c;用于确定源网络和目标网络之间的最佳路径。 文章目录 什么是…...

Linux 终端查看 nvidia 显卡型号

文章目录 写在前面1. 需求描述2. 实现方法方法一&#xff1a;方法二方法三&#xff1a; 参考链接 写在前面 自己的测试环境&#xff1a; Ubuntu20.04 1. 需求描述 Linux 终端查看 nvidia 显卡型号 2. 实现方法 方法一&#xff1a; 执行下列指令&#xff1a; sudo update…...

基于neurokit2的心电仿真数据生成实例解析

一 概念 NeuroKit2是一个开源的、社区驱动的、以用户为中心的Python库&#xff0c;可用于多种生理信号的分析处理&#xff08;例如ECG、PPG、EDA、EMG、RSP&#xff09;&#xff0c;还包括用于特定处理步骤&#xff08;如频率&#xff09;的工具提取和过滤方法&#xff0c;并在…...

AMBA-APB

目录 1.APB 协议 2.APB信号列表 3.数据传输 3.1写传输&#xff08;2种&#xff09; 3.1.1 无等待状态的写传输 3.1.2有等待状态的写传输 3.2写选通信号 (PSTRB) 字节通道映射 3.3读传输&#xff08;2种&#xff09; 3.3.1 无等待状态的读传输 3.3.2有等待状态的读传…...

网安入门之PHP后端基础

PHP 基本概念详解 PHP是一种服务器端脚本语言&#xff0c;常用于动态网站开发和 web 应用程序。以下是 PHP 的基本概念与特点的详细说明&#xff1a; 1. PHP 文件的默认文件扩展名 PHP 文件的扩展名通常为 .php&#xff0c;例如 index.php。PHP 文件可以包含 PHP 代码、HTML…...

windows系统安装完Anaconda之后怎么激活自己的虚拟环境并打开jupyter

1.在win主菜单中找到Anaconda安装文件夹并打开终端 文件夹内有所有安装后的Anaconda的应用软件和终端窗口启动窗口 点击Anaconda Prompt&#xff08;Anaconda&#xff09;就会打开类似cmd的命令终端窗口&#xff0c;默认打开的路径是用户名下的路径 2.激活虚拟环境 使用命令…...

【C#特性整理】C#特性及语法基础

1. C#特性 1.1 统一的类型系统 C#中, 所有类型都共享一个公共的基类型. 例如&#xff0c;任何类型的实例都可以通过调用ToString方法将自身转换为一个字符串 1.2 类和接口 接口: 用于将标准与实现隔离, 仅仅定义行为,不做实现. 1.3 属性、方法、事件 属性: 封装了一部分对…...

龙蜥 配置本地yum源8.6----亲测

系统版本: 说明:丫丫的,内网服务安装了个龙蜥操作系统。内网不能访问互联网,所以就需要挂载yum源 用的以前的方法挂载,一直报错 [Couldnt open file /mnt/anolisos/repodata/repomd.xml] yum挂载的时候报错,奇葩!!!,龙蜥的挂载方法好像不一样。 解决: 一、上传并挂…...

Xshell日志录制

步骤 1&#xff1a;设置日志文件位置 打开 Xshell 软件&#xff0c;选择目标会话&#xff08;或者创建一个新的会话&#xff09;。右键点击会话名称&#xff0c;选择“属性”。在会话属性窗口中&#xff0c;找到“日志”选项卡。勾选“启用日志记录”。在“文件名”中设置日志…...

vue下载和上传的地址动态ip地址配置方法

vue3结合element-plus实现【下载文件】和【上传文件】的动态ip地址配置 效果图 一、修改【文件上传】静态地址 1、首先引入axios import axios from "/utils/request"; import { getToken } from "/utils/auth"; 定义 const importDialogVisible ref(…...

动手做计算机网络仿真实验入门学习

打开软件 work1 添加串行接口模块&#xff0c;先关电源&#xff0c;添加之后再开电源 自动选择连接 所有传输介质 自动连接 串行线 绿色是通的&#xff0c;红色是不通的。 显示接口。se是serial串行的简写。 Fa是fast ethernet的简写。 为计算机配置ip地址&#xff1a; 为服…...

LeetCode 面试题 17.10. 主要元素

LeetCode 面试题 17.10. 主要元素 数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组&#xff0c;找出其中的主要元素。若没有&#xff0c;返回 -1 。请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。 示例 1&#xff1a; 输入&#xff1a;[1,2,5,9,5,9,5…...

Hive之import和export使用详解

在hive-0.8.0后引入了import/export命令。 Export命令可以导出一张表或分区的数据和元数据信息到一个输出位置&#xff0c;并且导出数据可以被移动到另一个hadoop集群或hive实例&#xff0c;并且可以通过import命令导入数据。 当导出一个分区表&#xff0c;原始数据可能在hdf…...