dev #1
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | model_data/yolov5_s_v6.1.pth filter=lfs diff=lfs merge=lfs -text | ||||||
							
								
								
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,10 @@ | |||||||
| database/chestXray8_512/* | database/chestXray8_512/* | ||||||
|  | database/* | ||||||
|  | logs/* | ||||||
|  |  | ||||||
|  | __pycache__ | ||||||
|  | */__pycache__ | ||||||
|  |  | ||||||
|  | .vscode/ | ||||||
|  |  | ||||||
|  | !*.md | ||||||
| @@ -5,5 +5,4 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| # 数据集介绍 | # 数据集介绍 | ||||||
|  | [详细见](./database/Dataset.md) | ||||||
| 数据集来自kaggle[数据集原地址] |  | ||||||
|   | |||||||
| @@ -43,3 +43,6 @@ mixup_prob          = 0.5 | |||||||
| special_aug_ratio   = 0.7 | special_aug_ratio   = 0.7 | ||||||
| ; label_smoothing     标签平滑。一般0.01以下。如0.01、0.005。 | ; label_smoothing     标签平滑。一般0.01以下。如0.01、0.005。 | ||||||
| label_smoothing     = 0.01 | label_smoothing     = 0.01 | ||||||
|  |  | ||||||
|  | [dataset] | ||||||
|  | classes_path        = model_data/voc_classes.txt | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								database/Dataset.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								database/Dataset.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | # 数据集介绍 | ||||||
|  |  | ||||||
|  | 数据集来自kaggle[数据集原地址](https://www.kaggle.com/datasets/spritan1/yolo-annotated-chestxray-8-object-detection) | ||||||
|  |  | ||||||
|  | ## 标签 | ||||||
|  | 1. 肺不张,Atelectasis | ||||||
|  | 2. 心脏肥大,Cardiomegaly | ||||||
|  | 3. 很有效率,Effusion | ||||||
|  | 4. 渗透,Infiltrate | ||||||
|  | 5. 诺依,Nodule | ||||||
|  | 6. 质量,Mass | ||||||
|  | 7. 肺炎,Pneumonia | ||||||
|  | 8. 气胸,Pneumothorax | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | # 存放数据集 | ||||||
							
								
								
									
										138
									
								
								get_map.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								get_map.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | |||||||
|  | import os | ||||||
|  | import xml.etree.ElementTree as ET | ||||||
|  |  | ||||||
|  | from PIL import Image | ||||||
|  | from tqdm import tqdm | ||||||
|  |  | ||||||
|  | from utils.utils import get_classes | ||||||
|  | from utils.utils_map import get_coco_map, get_map | ||||||
|  | from yolo import YOLO | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     ''' | ||||||
|  |     Recall和Precision不像AP是一个面积的概念,因此在门限值(Confidence)不同时,网络的Recall和Precision值是不同的。 | ||||||
|  |     默认情况下,本代码计算的Recall和Precision代表的是当门限值(Confidence)为0.5时,所对应的Recall和Precision值。 | ||||||
|  |  | ||||||
|  |     受到mAP计算原理的限制,网络在计算mAP时需要获得近乎所有的预测框,这样才可以计算不同门限条件下的Recall和Precision值 | ||||||
|  |     因此,本代码获得的map_out/detection-results/里面的txt的框的数量一般会比直接predict多一些,目的是列出所有可能的预测框, | ||||||
|  |     ''' | ||||||
|  |     #------------------------------------------------------------------------------------------------------------------# | ||||||
|  |     #   map_mode用于指定该文件运行时计算的内容 | ||||||
|  |     #   map_mode为0代表整个map计算流程,包括获得预测结果、获得真实框、计算VOC_map。 | ||||||
|  |     #   map_mode为1代表仅仅获得预测结果。 | ||||||
|  |     #   map_mode为2代表仅仅获得真实框。 | ||||||
|  |     #   map_mode为3代表仅仅计算VOC_map。 | ||||||
|  |     #   map_mode为4代表利用COCO工具箱计算当前数据集的0.50:0.95map。需要获得预测结果、获得真实框后并安装pycocotools才行 | ||||||
|  |     #-------------------------------------------------------------------------------------------------------------------# | ||||||
|  |     map_mode        = 0 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     #   此处的classes_path用于指定需要测量VOC_map的类别 | ||||||
|  |     #   一般情况下与训练和预测所用的classes_path一致即可 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     classes_path    = 'model_data/voc_classes.txt' | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     #   MINOVERLAP用于指定想要获得的mAP0.x,mAP0.x的意义是什么请同学们百度一下。 | ||||||
|  |     #   比如计算mAP0.75,可以设定MINOVERLAP = 0.75。 | ||||||
|  |     # | ||||||
|  |     #   当某一预测框与真实框重合度大于MINOVERLAP时,该预测框被认为是正样本,否则为负样本。 | ||||||
|  |     #   因此MINOVERLAP的值越大,预测框要预测的越准确才能被认为是正样本,此时算出来的mAP值越低, | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     MINOVERLAP      = 0.5 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     #   受到mAP计算原理的限制,网络在计算mAP时需要获得近乎所有的预测框,这样才可以计算mAP | ||||||
|  |     #   因此,confidence的值应当设置的尽量小进而获得全部可能的预测框。 | ||||||
|  |     #    | ||||||
|  |     #   该值一般不调整。因为计算mAP需要获得近乎所有的预测框,此处的confidence不能随便更改。 | ||||||
|  |     #   想要获得不同门限值下的Recall和Precision值,请修改下方的score_threhold。 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     confidence      = 0.001 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     #   预测时使用到的非极大抑制值的大小,越大表示非极大抑制越不严格。 | ||||||
|  |     #    | ||||||
|  |     #   该值一般不调整。 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     nms_iou         = 0.5 | ||||||
|  |     #---------------------------------------------------------------------------------------------------------------# | ||||||
|  |     #   Recall和Precision不像AP是一个面积的概念,因此在门限值不同时,网络的Recall和Precision值是不同的。 | ||||||
|  |     #    | ||||||
|  |     #   默认情况下,本代码计算的Recall和Precision代表的是当门限值为0.5(此处定义为score_threhold)时所对应的Recall和Precision值。 | ||||||
|  |     #   因为计算mAP需要获得近乎所有的预测框,上面定义的confidence不能随便更改。 | ||||||
|  |     #   这里专门定义一个score_threhold用于代表门限值,进而在计算mAP时找到门限值对应的Recall和Precision值。 | ||||||
|  |     #---------------------------------------------------------------------------------------------------------------# | ||||||
|  |     score_threhold  = 0.5 | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     #   map_vis用于指定是否开启VOC_map计算的可视化 | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     map_vis         = False | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     #   指向VOC数据集所在的文件夹 | ||||||
|  |     #   默认指向根目录下的VOC数据集 | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     VOCdevkit_path  = 'VOCdevkit' | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     #   结果输出的文件夹,默认为map_out | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     map_out_path    = 'map_out' | ||||||
|  |  | ||||||
|  |     image_ids = open(os.path.join(VOCdevkit_path, "VOC2007/ImageSets/Main/test.txt")).read().strip().split() | ||||||
|  |  | ||||||
|  |     if not os.path.exists(map_out_path): | ||||||
|  |         os.makedirs(map_out_path) | ||||||
|  |     if not os.path.exists(os.path.join(map_out_path, 'ground-truth')): | ||||||
|  |         os.makedirs(os.path.join(map_out_path, 'ground-truth')) | ||||||
|  |     if not os.path.exists(os.path.join(map_out_path, 'detection-results')): | ||||||
|  |         os.makedirs(os.path.join(map_out_path, 'detection-results')) | ||||||
|  |     if not os.path.exists(os.path.join(map_out_path, 'images-optional')): | ||||||
|  |         os.makedirs(os.path.join(map_out_path, 'images-optional')) | ||||||
|  |  | ||||||
|  |     class_names, _ = get_classes(classes_path) | ||||||
|  |  | ||||||
|  |     if map_mode == 0 or map_mode == 1: | ||||||
|  |         print("Load model.") | ||||||
|  |         yolo = YOLO(confidence = confidence, nms_iou = nms_iou) | ||||||
|  |         print("Load model done.") | ||||||
|  |  | ||||||
|  |         print("Get predict result.") | ||||||
|  |         for image_id in tqdm(image_ids): | ||||||
|  |             image_path  = os.path.join(VOCdevkit_path, "VOC2007/JPEGImages/"+image_id+".jpg") | ||||||
|  |             image       = Image.open(image_path) | ||||||
|  |             if map_vis: | ||||||
|  |                 image.save(os.path.join(map_out_path, "images-optional/" + image_id + ".jpg")) | ||||||
|  |             yolo.get_map_txt(image_id, image, class_names, map_out_path) | ||||||
|  |         print("Get predict result done.") | ||||||
|  |          | ||||||
|  |     if map_mode == 0 or map_mode == 2: | ||||||
|  |         print("Get ground truth result.") | ||||||
|  |         for image_id in tqdm(image_ids): | ||||||
|  |             with open(os.path.join(map_out_path, "ground-truth/"+image_id+".txt"), "w") as new_f: | ||||||
|  |                 root = ET.parse(os.path.join(VOCdevkit_path, "VOC2007/Annotations/"+image_id+".xml")).getroot() | ||||||
|  |                 for obj in root.findall('object'): | ||||||
|  |                     difficult_flag = False | ||||||
|  |                     if obj.find('difficult')!=None: | ||||||
|  |                         difficult = obj.find('difficult').text | ||||||
|  |                         if int(difficult)==1: | ||||||
|  |                             difficult_flag = True | ||||||
|  |                     obj_name = obj.find('name').text | ||||||
|  |                     if obj_name not in class_names: | ||||||
|  |                         continue | ||||||
|  |                     bndbox  = obj.find('bndbox') | ||||||
|  |                     left    = bndbox.find('xmin').text | ||||||
|  |                     top     = bndbox.find('ymin').text | ||||||
|  |                     right   = bndbox.find('xmax').text | ||||||
|  |                     bottom  = bndbox.find('ymax').text | ||||||
|  |  | ||||||
|  |                     if difficult_flag: | ||||||
|  |                         new_f.write("%s %s %s %s %s difficult\n" % (obj_name, left, top, right, bottom)) | ||||||
|  |                     else: | ||||||
|  |                         new_f.write("%s %s %s %s %s\n" % (obj_name, left, top, right, bottom)) | ||||||
|  |         print("Get ground truth result done.") | ||||||
|  |  | ||||||
|  |     if map_mode == 0 or map_mode == 3: | ||||||
|  |         print("Get map.") | ||||||
|  |         get_map(MINOVERLAP, True, score_threhold = score_threhold, path = map_out_path) | ||||||
|  |         print("Get map done.") | ||||||
|  |  | ||||||
|  |     if map_mode == 4: | ||||||
|  |         print("Get map.") | ||||||
|  |         get_coco_map(class_names = class_names, path = map_out_path) | ||||||
|  |         print("Get map done.") | ||||||
							
								
								
									
										639
									
								
								model_data/2007_train.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										639
									
								
								model_data/2007_train.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,639 @@ | |||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000002_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000005_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000005_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000005_004.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000005_005.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000006_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000007_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000008_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000011_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000011_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000011_003.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000011_004.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000011_008.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000013_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000013_008.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000013_014.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000013_015.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000013_016.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000013_017.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000013_019.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000014_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000015_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000016_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000017_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000017_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000018_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000022_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000023_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000023_003.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000029_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_003.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_005.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_009.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_010.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_019.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_020.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_022.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_025.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_046.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_048.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_049.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_051.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000034_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000035_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000037_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000038_004.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000039_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000039_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000039_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000039_003.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000040_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000040_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000041_003.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000041_004.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000042_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000042_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000042_004.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000042_008.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000046_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000047_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000047_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000047_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000047_005.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000047_007.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000048_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000049_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000049_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000050_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000050_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000050_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000050_003.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000052_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000052_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000054_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000054_002.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000054_003.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000054_005.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000054_008.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000055_000.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000072_000.png 178,283,242,325,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000147_001.png 308,299,359,389,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000149_006.png 301,230,426,275,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000181_061.png 104,284,211,411,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000211_010.png 176,267,227,351,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000211_019.png 170,211,420,414,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000211_041.png 112,208,368,423,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000344_003.png 86,115,135,226,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000377_004.png 193,209,445,393,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000398_003.png 184,194,426,382,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000457_004.png 401,140,448,193,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000468_017.png 74,259,130,282,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000468_033.png 102,251,160,277,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000583_008.png 146,227,251,257,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000643_002.png 409,269,457,307,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000661_000.png 149,218,423,432,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000732_005.png 213,232,420,404,0 306,55,393,107,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000740_000.png 150,192,389,385,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000744_006.png 73,254,144,314,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000756_001.png 144,162,445,377,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000808_002.png 279,192,392,275,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000830_000.png 287,244,428,302,3 154,199,203,259,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000845_000.png 185,183,412,396,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000865_006.png 419,312,458,332,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000902_001.png 175,317,219,359,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001075_024.png 145,163,222,262,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001153_004.png 153,126,202,346,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001170_046.png 317,191,440,288,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001248_038.png 283,47,410,131,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001369_000.png 168,138,445,331,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001373_009.png 206,234,480,484,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001373_039.png 130,250,411,495,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001534_005.png 198,171,443,361,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001555_002.png 307,404,344,442,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001558_016.png 42,209,156,417,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001673_016.png 140,274,166,324,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001836_041.png 313,192,329,205,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001836_082.png 85,284,166,339,3 168,194,238,251,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001946_029.png 144,198,181,275,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002059_008.png 187,232,464,415,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002106_000.png 162,181,188,207,2 318,214,383,291,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002176_007.png 129,94,183,152,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002290_001.png 130,358,161,382,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002350_001.png 363,240,437,284,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002533_002.png 140,13,244,280,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002583_014.png 304,198,463,402,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002763_031.png 201,177,439,371,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003072_028.png 12,169,154,394,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003148_004.png 298,252,326,342,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003394_006.png 156,187,377,339,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003400_003.png 304,28,424,142,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003440_000.png 108,127,170,167,3 295,228,353,285,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003787_003.png 144,235,228,294,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003803_010.png 85,295,405,361,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003945_004.png 34,235,187,319,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00003948_001.png 140,152,192,216,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004296_000.png 365,376,425,423,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004342_002.png 100,243,339,483,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004342_050.png 92,180,160,391,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004344_013.png 175,173,437,384,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004344_014.png 164,156,398,381,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004344_018.png 195,196,448,411,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004344_022.png 188,254,420,441,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004344_046.png 184,166,424,353,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004381_021.png 194,180,421,343,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004461_000.png 164,196,370,370,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004533_014.png 225,153,449,349,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004534_001.png 128,214,403,422,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004547_003.png 449,345,476,374,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004578_004.png 216,195,432,381,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004630_001.png 142,219,398,391,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004822_051.png 179,184,441,381,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004893_085.png 192,171,424,366,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004911_018.png 347,159,398,204,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004968_003.png 320,342,453,395,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004968_004.png 317,318,449,380,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005066_005.png 165,243,419,418,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005066_030.png 138,229,408,380,0 74,364,88,397,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005089_002.png 324,287,455,381,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005089_014.png 89,238,159,274,3 322,93,436,391,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005089_040.png 309,49,434,389,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005140_001.png 62,131,132,354,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005532_000.png 191,131,429,338,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005532_014.png 169,165,399,338,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005532_016.png 162,208,369,378,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005532_019.png 341,117,377,146,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005827_000.png 196,153,422,389,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00005869_001.png 90,33,213,73,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00006736_000.png 385,236,407,259,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00006751_000.png 87,222,125,255,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00006821_002.png 60,42,251,380,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00006851_033.png 94,312,194,339,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00006851_034.png 125,308,205,369,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00006912_007.png 165,196,385,359,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007037_000.png 144,243,394,436,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007043_000.png 145,180,410,399,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007120_009.png 124,274,192,318,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007124_008.png 272,244,426,337,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007471_003.png 270,53,437,294,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007551_020.png 176,261,405,436,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007557_026.png 65,353,176,415,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007676_002.png 180,219,221,288,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007710_000.png 312,219,420,263,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007735_040.png 199,238,470,462,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007882_001.png 75,321,176,350,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008008_027.png 323,244,371,295,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008339_010.png 174,173,437,366,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008365_000.png 168,254,377,456,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008386_000.png 355,178,385,202,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008399_007.png 181,189,455,375,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008522_032.png 157,104,374,301,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008547_001.png 403,340,474,411,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008554_009.png 303,247,418,331,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008716_000.png 353,354,429,406,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008814_010.png 97,203,432,236,3 97,206,430,259,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008841_025.png 77,243,210,293,1 292,40,388,83,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00008841_044.png 73,23,235,106,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009166_004.png 78,121,119,273,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009218_015.png 96,109,207,405,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009229_007.png 361,273,396,310,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009256_005.png 152,196,208,254,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009342_000.png 429,214,506,340,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009403_006.png 374,208,412,234,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009437_008.png 376,297,457,336,3 371,290,465,429,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009608_024.png 197,201,379,372,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009619_000.png 150,329,204,385,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009669_003.png 78,324,209,434,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009683_005.png 16,179,153,330,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009705_000.png 169,154,440,339,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009745_000.png 164,199,392,391,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009779_001.png 355,247,437,281,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009889_018.png 62,379,79,399,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010007_168.png 166,360,194,420,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010103_014.png 171,206,192,229,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010125_004.png 73,150,106,194,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010172_001.png 94,347,118,373,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010277_000.png 431,346,467,402,1 148,155,418,294,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010381_000.png 195,156,422,336,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010478_012.png 106,293,229,394,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010481_021.png 381,275,461,310,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010575_002.png 116,302,233,381,3 347,295,471,383,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010625_014.png 135,221,408,290,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010767_008.png 101,95,184,166,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010767_016.png 58,88,214,384,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010770_000.png 122,185,225,219,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010815_006.png 155,120,232,242,3 155,120,228,248,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010828_039.png 294,234,400,348,3 351,223,470,342,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010936_011.png 135,290,187,389,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010936_016.png 87,82,203,151,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010959_010.png 106,245,145,275,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011151_004.png 165,103,353,286,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011157_001.png 100,245,163,306,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011237_094.png 64,114,144,369,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011263_004.png 156,196,433,437,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011269_019.png 366,209,475,303,1 286,133,351,204,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011322_006.png 182,242,423,399,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011402_007.png 102,240,349,412,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011450_000.png 367,247,416,295,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011463_002.png 189,272,410,444,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011502_001.png 221,158,404,349,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011576_000.png 103,291,126,312,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011814_031.png 84,227,178,377,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011827_003.png 284,235,437,297,3 376,74,440,366,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011831_010.png 117,41,237,127,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011832_002.png 372,175,407,212,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011857_001.png 357,268,462,309,3 347,256,481,339,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011925_072.png 188,127,269,329,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011925_076.png 61,53,209,240,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012045_009.png 358,280,408,339,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012048_007.png 143,190,217,266,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012094_040.png 288,55,361,108,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012123_001.png 319,305,456,413,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012174_000.png 84,79,178,349,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012261_001.png 226,216,493,426,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012270_005.png 371,117,428,173,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012291_008.png 116,315,229,353,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012374_000.png 327,128,368,168,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012376_010.png 370,105,433,317,2 187,71,247,99,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012376_011.png 385,240,422,279,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012415_002.png 199,267,236,310,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012505_007.png 127,84,246,350,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012515_002.png 144,319,186,347,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012622_016.png 381,83,422,147,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012637_000.png 364,249,430,291,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012670_000.png 213,210,428,368,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012686_003.png 162,205,430,406,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012741_004.png 221,217,444,421,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012793_000.png 176,189,423,391,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012834_008.png 55,206,154,242,3 2,179,139,390,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012834_034.png 384,113,487,292,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012834_122.png 38,47,167,484,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012892_010.png 187,112,240,179,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012973_005.png 61,31,201,412,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012975_003.png 103,118,206,219,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013031_005.png 9,209,89,406,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013051_000.png 133,275,162,303,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013111_069.png 237,201,309,331,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013118_008.png 112,273,155,313,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013125_000.png 84,203,415,412,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013187_002.png 99,283,468,371,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013249_031.png 191,239,393,368,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013249_033.png 207,201,441,373,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013285_026.png 89,249,108,383,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013337_000.png 30,157,169,381,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013346_015.png 186,211,416,472,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013471_002.png 64,346,199,396,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013508_001.png 161,296,250,341,3 143,258,188,305,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013615_052.png 175,203,407,381,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013635_002.png 181,165,412,350,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013659_019.png 279,83,330,152,2 145,42,199,67,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013674_000.png 105,159,134,188,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013685_028.png 132,246,392,318,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013750_016.png 98,91,195,228,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013751_003.png 338,305,383,350,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013807_009.png 157,61,218,82,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013885_000.png 218,136,240,158,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013911_000.png 59,241,101,292,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013951_001.png 150,133,182,158,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013977_005.png 390,392,452,482,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013991_000.png 190,185,243,232,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013992_006.png 140,263,204,303,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013993_077.png 83,203,184,315,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013993_083.png 68,108,196,457,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014004_038.png 172,198,227,267,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014014_013.png 324,191,409,276,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014083_023.png 320,305,458,371,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014095_003.png 148,248,234,295,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014116_009.png 348,159,375,188,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014177_010.png 94,194,158,310,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014223_009.png 112,193,354,337,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014253_010.png 18,123,141,165,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014346_010.png 76,72,219,379,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014398_031.png 94,231,189,395,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014447_004.png 106,210,188,258,3 77,214,124,348,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014551_010.png 293,205,372,295,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014574_000.png 184,182,403,380,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014607_007.png 100,330,421,437,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014626_017.png 135,67,186,142,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014626_035.png 75,85,183,478,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014687_001.png 363,247,433,275,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014706_018.png 185,223,397,368,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014716_007.png 343,65,435,222,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014731_028.png 386,132,443,203,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014738_000.png 95,60,220,350,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014795_002.png 144,270,218,337,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014822_039.png 322,102,419,414,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014870_004.png 57,306,169,353,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015018_004.png 402,180,430,209,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015141_002.png 398,301,423,334,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015262_005.png 68,267,143,315,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015300_000.png 85,197,159,271,3 107,237,146,264,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015304_001.png 145,167,453,414,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015400_001.png 149,175,389,380,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015440_000.png 318,152,394,186,3 87,146,241,404,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015530_147.png 93,33,195,340,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015583_000.png 339,170,363,188,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015649_000.png 320,149,363,186,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015719_005.png 176,189,426,461,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015770_010.png 181,235,407,370,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015792_005.png 133,146,173,185,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015794_000.png 34,324,58,346,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015799_012.png 180,231,442,430,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016009_008.png 286,288,363,412,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016184_040.png 349,269,371,295,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016191_017.png 341,296,441,378,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016267_000.png 89,299,205,408,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016291_002.png 308,338,471,377,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016291_019.png 76,37,214,219,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016414_000.png 192,215,478,439,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016417_008.png 392,126,483,436,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016429_015.png 73,46,263,331,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016487_002.png 320,346,364,385,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016490_011.png 91,261,175,293,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016522_023.png 68,125,236,447,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016568_010.png 345,200,399,262,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016606_000.png 183,212,427,402,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016837_002.png 70,329,206,376,3 70,352,199,383,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016937_014.png 86,41,218,145,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016964_011.png 364,276,411,306,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016972_019.png 257,317,406,431,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016972_025.png 344,262,404,360,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016987_019.png 132,303,185,342,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016987_022.png 41,244,190,305,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016990_000.png 156,237,385,388,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017028_000.png 72,289,111,319,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017124_004.png 396,284,478,505,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017137_016.png 85,73,228,311,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017151_003.png 312,244,418,343,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017178_007.png 172,150,395,339,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017188_002.png 99,240,214,285,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017199_005.png 386,341,422,378,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017214_015.png 306,174,346,246,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017243_010.png 38,301,82,342,2 10,145,73,428,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017255_001.png 346,299,407,343,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017257_001.png 213,112,372,287,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017346_000.png 436,307,450,324,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017500_002.png 382,307,435,338,3 55,248,154,384,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017514_008.png 173,222,441,390,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017524_028.png 125,217,374,432,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017582_003.png 128,201,228,273,3 53,171,111,326,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017611_002.png 129,224,210,299,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017670_005.png 184,295,244,369,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017714_006.png 406,254,468,418,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017747_008.png 114,87,215,166,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017877_001.png 330,284,430,323,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017893_005.png 193,182,439,386,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017972_026.png 331,123,377,226,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018055_005.png 67,66,238,301,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018055_045.png 44,154,67,204,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018101_012.png 86,92,215,254,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018102_001.png 53,294,73,321,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018187_034.png 240,198,454,368,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018233_057.png 173,192,379,366,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018253_017.png 274,264,355,367,3 67,131,141,397,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018253_059.png 92,202,194,346,3 89,191,190,339,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018366_000.png 95,303,127,338,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018366_029.png 280,235,405,268,1 297,64,399,162,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018387_030.png 194,179,442,326,0 79,220,463,301,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018427_004.png 47,315,135,352,3 385,175,477,449,1 266,164,319,214,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018427_011.png 297,343,449,393,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018496_006.png 40,354,476,464,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018623_001.png 138,249,444,378,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018657_003.png 361,224,446,371,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018686_000.png 176,153,425,380,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018693_004.png 150,217,424,417,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018762_001.png 341,331,382,369,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018762_002.png 301,308,430,349,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018814_000.png 344,257,394,303,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018865_008.png 104,66,177,112,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018980_002.png 123,87,213,197,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018984_000.png 412,154,449,212,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019013_002.png 391,279,416,301,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019058_004.png 175,229,221,299,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019089_004.png 287,312,409,405,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019124_045.png 166,260,229,294,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019124_090.png 328,247,376,284,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019124_104.png 59,69,206,239,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019154_002.png 146,62,212,171,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019187_000.png 198,191,430,384,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019271_030.png 117,348,194,404,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019271_064.png 132,254,429,388,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019271_065.png 105,249,176,305,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019373_036.png 333,203,419,259,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019373_058.png 162,165,224,243,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019399_010.png 391,301,458,425,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019426_000.png 224,220,422,356,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019495_004.png 369,282,443,302,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019499_000.png 361,156,417,204,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019706_002.png 288,44,364,104,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019706_014.png 331,217,417,288,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019766_023.png 367,208,463,366,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019767_016.png 64,318,223,361,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019861_010.png 134,178,402,377,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019892_003.png 285,59,412,109,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019924_020.png 315,314,410,504,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020000_000.png 271,292,382,436,1 135,66,179,80,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020065_008.png 409,158,453,228,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020113_017.png 108,257,448,341,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020124_003.png 87,290,209,341,3 180,142,234,210,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020146_005.png 90,281,239,396,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020213_078.png 92,335,211,385,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020277_001.png 346,214,448,410,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020318_007.png 357,271,467,329,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020318_022.png 275,43,413,163,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020332_000.png 114,311,152,343,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020349_006.png 231,155,371,303,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020393_001.png 118,113,156,148,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020393_003.png 340,270,451,319,3 341,251,467,343,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020405_041.png 55,143,154,332,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020408_037.png 95,316,209,396,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020438_011.png 164,180,407,420,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020482_032.png 241,176,346,357,3 215,212,356,343,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020564_000.png 387,179,444,299,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020673_005.png 281,163,410,248,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020751_003.png 373,77,497,406,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020774_000.png 114,238,145,270,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020810_003.png 79,357,209,414,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020819_002.png 184,170,431,368,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020857_008.png 95,285,376,387,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020986_000.png 209,190,429,336,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021007_000.png 172,234,224,285,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021009_001.png 159,182,389,337,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021024_022.png 359,261,441,356,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021132_000.png 352,102,432,340,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021179_011.png 62,86,239,420,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021181_002.png 75,195,218,298,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021201_010.png 357,95,435,254,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021321_002.png 95,22,197,158,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021374_000.png 373,188,433,241,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021377_016.png 361,336,449,387,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021381_013.png 281,289,422,334,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021420_014.png 393,135,429,174,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021420_020.png 83,345,129,390,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021420_027.png 96,282,149,326,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021443_000.png 143,186,415,393,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021481_014.png 297,200,347,280,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021700_010.png 293,41,394,124,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021703_001.png 68,250,376,356,3 281,255,385,333,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021711_014.png 275,334,451,395,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021748_000.png 275,37,358,64,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021772_016.png 85,96,131,137,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021782_028.png 66,49,177,270,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021796_000.png 93,205,181,295,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021840_016.png 71,268,192,317,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021845_001.png 153,186,347,368,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021860_002.png 50,262,121,367,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021896_003.png 119,68,232,176,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021967_000.png 91,46,273,339,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022141_023.png 322,48,428,113,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022155_008.png 342,138,420,177,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022192_003.png 87,110,218,262,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022215_012.png 161,176,418,385,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022237_002.png 134,236,198,287,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022369_013.png 83,199,170,293,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022416_004.png 409,234,481,334,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022416_018.png 178,211,462,388,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022416_049.png 394,213,436,278,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022572_063.png 35,258,176,418,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022572_087.png 10,122,117,353,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022611_001.png 374,84,459,272,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022706_001.png 173,157,398,351,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022707_003.png 377,284,463,320,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022883_002.png 57,255,423,309,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022977_000.png 120,321,153,353,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023058_004.png 19,281,177,382,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023068_003.png 175,373,193,390,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023075_033.png 119,267,156,300,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023078_000.png 370,343,396,367,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023078_003.png 47,330,112,387,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023093_007.png 345,206,458,377,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023093_009.png 216,176,410,312,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023156_001.png 114,115,192,173,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023162_025.png 96,39,228,168,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023168_000.png 107,339,154,400,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023176_010.png 140,252,478,331,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023176_017.png 161,127,428,340,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023325_019.png 172,140,410,315,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025221_001.png 269,291,441,451,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025228_005.png 295,229,443,349,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025228_007.png 59,235,130,386,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025252_032.png 378,340,478,462,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025252_040.png 300,101,431,310,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025252_053.png 7,320,175,400,1 325,319,397,451,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025252_054.png 329,360,401,472,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025270_000.png 134,260,184,294,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025368_014.png 147,342,205,384,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025368_018.png 75,61,235,197,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025368_033.png 379,139,478,236,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025521_003.png 36,260,89,390,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025529_018.png 362,83,454,385,1 351,194,406,258,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025662_006.png 413,319,439,348,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025664_002.png 33,72,171,387,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025686_000.png 298,335,464,387,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025732_004.png 109,195,378,403,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025747_000.png 167,185,415,369,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025769_001.png 350,286,402,318,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025787_027.png 302,49,490,418,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025787_039.png 43,49,134,334,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025787_050.png 305,90,333,215,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025954_025.png 98,46,235,171,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026087_000.png 169,231,378,428,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026136_002.png 275,277,414,332,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026196_001.png 196,245,238,285,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026221_001.png 99,364,151,422,2 68,75,228,328,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026285_000.png 452,412,476,436,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026319_000.png 334,326,394,365,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026338_003.png 176,172,395,330,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026392_005.png 281,55,440,183,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026398_000.png 182,132,206,156,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026451_068.png 147,321,209,368,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026586_009.png 98,93,183,158,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026695_000.png 170,98,229,164,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026753_008.png 299,53,450,393,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026769_010.png 396,208,423,235,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026810_001.png 383,354,442,381,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026886_002.png 112,98,229,437,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026886_004.png 117,258,224,326,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026889_000.png 118,185,415,412,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026911_005.png 173,153,216,201,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026920_000.png 171,223,232,249,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026983_001.png 276,95,310,131,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027028_017.png 75,260,461,330,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027094_003.png 384,280,461,356,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027103_001.png 336,118,438,253,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027213_044.png 285,83,389,489,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027278_007.png 76,23,232,162,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027357_014.png 31,52,162,375,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027441_002.png 60,67,169,348,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027470_006.png 199,250,247,287,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027474_005.png 138,203,188,235,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027577_003.png 47,105,115,238,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027631_000.png 418,356,473,427,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027652_003.png 37,101,110,358,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027685_003.png 170,161,404,352,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027697_001.png 354,192,451,221,3 79,244,234,350,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027797_000.png 215,204,462,365,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027833_022.png 150,152,445,373,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027866_002.png 72,308,373,416,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027875_005.png 348,156,481,357,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027927_009.png 369,195,395,226,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027937_004.png 83,263,138,314,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028012_001.png 280,231,443,331,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028018_000.png 176,226,404,440,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028027_000.png 287,123,346,195,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028173_016.png 282,250,338,319,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028330_003.png 104,219,177,317,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028383_002.png 389,385,433,414,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028452_001.png 314,205,442,279,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028509_007.png 24,169,128,451,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028509_026.png 155,200,408,371,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028518_021.png 92,339,185,392,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028607_000.png 202,151,450,372,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028620_000.png 399,334,476,376,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028625_000.png 169,225,229,273,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028628_008.png 303,49,409,210,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028628_015.png 84,76,170,281,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028628_020.png 313,169,365,211,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028640_008.png 381,271,448,405,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028698_001.png 425,202,455,286,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028861_009.png 290,23,436,173,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028873_009.png 166,140,364,319,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028876_027.png 268,56,384,128,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028876_035.png 95,170,127,206,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028924_009.png 365,112,427,242,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028974_016.png 384,290,467,389,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029075_013.png 328,67,453,182,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029088_023.png 152,147,225,208,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029105_015.png 114,214,174,275,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029200_006.png 66,254,144,311,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029259_027.png 393,193,450,339,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029391_000.png 157,185,400,373,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029464_006.png 45,142,131,239,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029464_015.png 99,176,407,338,3 359,305,455,331,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029469_011.png 362,215,448,441,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029502_006.png 110,294,236,384,3 74,226,202,423,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029532_005.png 64,49,206,280,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029579_014.png 50,260,69,322,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029588_004.png 20,99,153,381,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029631_006.png 59,254,145,307,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029647_002.png 167,203,415,396,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029807_003.png 82,41,260,192,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029817_009.png 110,158,188,267,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029843_001.png 128,224,193,279,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029894_000.png 357,254,472,363,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029906_000.png 165,224,381,382,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029909_003.png 83,84,200,129,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029940_007.png 106,191,178,282,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030039_008.png 13,60,145,373,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030106_008.png 112,258,227,393,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030111_007.png 296,48,375,174,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030162_029.png 163,190,197,227,2 310,83,412,163,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030206_013.png 156,169,389,386,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030260_004.png 184,275,212,386,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030323_015.png 307,211,420,357,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030394_001.png 311,91,344,120,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030408_013.png 359,302,418,374,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030434_000.png 333,322,371,442,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030606_006.png 275,15,393,96,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030636_004.png 305,220,385,295,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030674_000.png 316,125,430,231,3 | ||||||
							
								
								
									
										72
									
								
								model_data/2007_val.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								model_data/2007_val.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000005_001.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000005_003.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000013_038.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000032_008.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00000042_005.png | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001320_003.png 104,272,140,287,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00001437_012.png 41,69,218,377,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002176_005.png 130,44,223,122,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002395_007.png 115,224,153,303,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002435_005.png 181,240,382,389,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002578_000.png 398,163,437,193,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00002856_009.png 108,318,217,369,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00004342_020.png 158,193,395,409,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00006621_004.png 153,200,261,308,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00007034_016.png 383,288,428,376,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00009368_006.png 184,140,218,194,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00010071_008.png 101,340,137,431,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011291_003.png 346,73,441,175,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011322_002.png 173,178,426,420,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00011355_011.png 250,26,464,253,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012045_019.png 211,246,239,274,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012094_047.png 333,71,400,147,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012299_002.png 97,287,427,327,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00012829_004.png 84,269,168,316,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013310_059.png 96,104,190,388,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013673_001.png 75,217,115,253,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013685_000.png 129,218,185,243,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00013911_021.png 49,121,399,390,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014198_000.png 338,256,387,352,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014626_028.png 166,177,385,377,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00014976_003.png 303,340,424,380,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015090_006.png 319,285,417,374,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015425_012.png 192,144,415,322,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00015895_017.png 359,177,438,438,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016403_003.png 446,438,501,471,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016564_000.png 192,195,418,348,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016587_069.png 377,203,412,232,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00016624_000.png 276,173,460,339,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017138_037.png 59,143,154,384,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017403_006.png 297,288,393,440,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017448_000.png 177,241,416,437,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00017511_006.png 169,211,368,378,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018187_029.png 171,210,427,441,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018253_054.png 91,154,214,407,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018412_001.png 106,308,392,353,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00018496_007.png 286,62,397,109,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019313_000.png 374,307,423,361,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00019917_004.png 306,388,430,412,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020274_021.png 79,218,130,253,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020408_058.png 80,36,224,232,4 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00020429_020.png 83,295,174,338,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021364_001.png 167,225,415,378,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021409_001.png 156,172,402,365,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00021862_004.png 303,279,363,347,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022098_006.png 247,288,382,365,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00022726_002.png 344,200,407,260,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00023116_005.png 320,219,410,298,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00025969_000.png 182,142,401,332,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026132_016.png 350,241,390,278,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00026261_001.png 98,140,217,260,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027479_013.png 386,282,479,317,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027556_007.png 86,322,155,380,3 35,98,120,216,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00027817_001.png 380,195,478,378,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028265_007.png 399,303,442,353,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028873_004.png 160,160,380,328,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00028876_060.png 381,194,419,237,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029464_003.png 121,187,224,251,3 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00029808_003.png 160,205,384,402,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030279_000.png 144,210,418,438,0 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030413_003.png 127,118,172,157,2 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030634_000.png 70,305,229,354,1 | ||||||
|  | E:\project\python\rabat-illness-yolov5\database\Train/JPEGImages/00030635_001.png 288,245,408,356,3 | ||||||
| @@ -1,2 +1,5 @@ | |||||||
| 0 |  | ||||||
| 1 | 1 | ||||||
|  | 2 | ||||||
|  | 3 | ||||||
|  | 4 | ||||||
|  | 5 | ||||||
|   | |||||||
| @@ -1,2 +1,5 @@ | |||||||
| 0 |  | ||||||
| 1 | 1 | ||||||
|  | 2 | ||||||
|  | 3 | ||||||
|  | 4 | ||||||
|  | 5 | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										177
									
								
								nets/CSPdarknet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								nets/CSPdarknet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | |||||||
|  | import torch | ||||||
|  | import torch.nn as nn | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SiLU(nn.Module): | ||||||
|  |     @staticmethod | ||||||
|  |     def forward(x): | ||||||
|  |         return x * torch.sigmoid(x) | ||||||
|  |  | ||||||
|  | def autopad(k, p=None): | ||||||
|  |     if p is None: | ||||||
|  |         p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  | ||||||
|  |     return p | ||||||
|  |  | ||||||
|  | class Focus(nn.Module): | ||||||
|  |     def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups | ||||||
|  |         super(Focus, self).__init__() | ||||||
|  |         self.conv = Conv(c1 * 4, c2, k, s, p, g, act) | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         # 320, 320, 12 => 320, 320, 64 | ||||||
|  |         return self.conv( | ||||||
|  |             # 640, 640, 3 => 320, 320, 12 | ||||||
|  |             torch.cat( | ||||||
|  |                 [ | ||||||
|  |                     x[..., ::2, ::2],  | ||||||
|  |                     x[..., 1::2, ::2],  | ||||||
|  |                     x[..., ::2, 1::2],  | ||||||
|  |                     x[..., 1::2, 1::2] | ||||||
|  |                 ], 1 | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  | class Conv(nn.Module): | ||||||
|  |     def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): | ||||||
|  |         super(Conv, self).__init__() | ||||||
|  |         self.conv   = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) | ||||||
|  |         self.bn     = nn.BatchNorm2d(c2, eps=0.001, momentum=0.03) | ||||||
|  |         self.act    = SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         return self.act(self.bn(self.conv(x))) | ||||||
|  |  | ||||||
|  |     def fuseforward(self, x): | ||||||
|  |         return self.act(self.conv(x)) | ||||||
|  |  | ||||||
|  | class Bottleneck(nn.Module): | ||||||
|  |     # Standard bottleneck | ||||||
|  |     def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, shortcut, groups, expansion | ||||||
|  |         super(Bottleneck, self).__init__() | ||||||
|  |         c_ = int(c2 * e)  # hidden channels | ||||||
|  |         self.cv1 = Conv(c1, c_, 1, 1) | ||||||
|  |         self.cv2 = Conv(c_, c2, 3, 1, g=g) | ||||||
|  |         self.add = shortcut and c1 == c2 | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) | ||||||
|  |  | ||||||
|  | class C3(nn.Module): | ||||||
|  |     # CSP Bottleneck with 3 convolutions | ||||||
|  |     def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion | ||||||
|  |         super(C3, self).__init__() | ||||||
|  |         c_ = int(c2 * e)  # hidden channels | ||||||
|  |         self.cv1 = Conv(c1, c_, 1, 1) | ||||||
|  |         self.cv2 = Conv(c1, c_, 1, 1) | ||||||
|  |         self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2) | ||||||
|  |         self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) | ||||||
|  |         # self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)]) | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         return self.cv3(torch.cat( | ||||||
|  |             ( | ||||||
|  |                 self.m(self.cv1(x)),  | ||||||
|  |                 self.cv2(x) | ||||||
|  |             ) | ||||||
|  |             , dim=1)) | ||||||
|  |  | ||||||
|  | class SPP(nn.Module): | ||||||
|  |     # Spatial pyramid pooling layer used in YOLOv3-SPP | ||||||
|  |     def __init__(self, c1, c2, k=(5, 9, 13)): | ||||||
|  |         super(SPP, self).__init__() | ||||||
|  |         c_ = c1 // 2  # hidden channels | ||||||
|  |         self.cv1 = Conv(c1, c_, 1, 1) | ||||||
|  |         self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1) | ||||||
|  |         self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k]) | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         x = self.cv1(x) | ||||||
|  |         return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1)) | ||||||
|  |          | ||||||
|  | class CSPDarknet(nn.Module): | ||||||
|  |     def __init__(self, base_channels, base_depth, phi, pretrained): | ||||||
|  |         super().__init__() | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   输入图片是640, 640, 3 | ||||||
|  |         #   初始的基本通道base_channels是64 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |  | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   利用focus网络结构进行特征提取 | ||||||
|  |         #   640, 640, 3 -> 320, 320, 12 -> 320, 320, 64 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         self.stem       = Focus(3, base_channels, k=3) | ||||||
|  |          | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   完成卷积之后,320, 320, 64 -> 160, 160, 128 | ||||||
|  |         #   完成CSPlayer之后,160, 160, 128 -> 160, 160, 128 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         self.dark2 = nn.Sequential( | ||||||
|  |             # 320, 320, 64 -> 160, 160, 128 | ||||||
|  |             Conv(base_channels, base_channels * 2, 3, 2), | ||||||
|  |             # 160, 160, 128 -> 160, 160, 128 | ||||||
|  |             C3(base_channels * 2, base_channels * 2, base_depth), | ||||||
|  |         ) | ||||||
|  |          | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   完成卷积之后,160, 160, 128 -> 80, 80, 256 | ||||||
|  |         #   完成CSPlayer之后,80, 80, 256 -> 80, 80, 256 | ||||||
|  |         #                   在这里引出有效特征层80, 80, 256 | ||||||
|  |         #                   进行加强特征提取网络FPN的构建 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         self.dark3 = nn.Sequential( | ||||||
|  |             Conv(base_channels * 2, base_channels * 4, 3, 2), | ||||||
|  |             C3(base_channels * 4, base_channels * 4, base_depth * 3), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   完成卷积之后,80, 80, 256 -> 40, 40, 512 | ||||||
|  |         #   完成CSPlayer之后,40, 40, 512 -> 40, 40, 512 | ||||||
|  |         #                   在这里引出有效特征层40, 40, 512 | ||||||
|  |         #                   进行加强特征提取网络FPN的构建 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         self.dark4 = nn.Sequential( | ||||||
|  |             Conv(base_channels * 4, base_channels * 8, 3, 2), | ||||||
|  |             C3(base_channels * 8, base_channels * 8, base_depth * 3), | ||||||
|  |         ) | ||||||
|  |          | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   完成卷积之后,40, 40, 512 -> 20, 20, 1024 | ||||||
|  |         #   完成SPP之后,20, 20, 1024 -> 20, 20, 1024 | ||||||
|  |         #   完成CSPlayer之后,20, 20, 1024 -> 20, 20, 1024 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         self.dark5 = nn.Sequential( | ||||||
|  |             Conv(base_channels * 8, base_channels * 16, 3, 2), | ||||||
|  |             SPP(base_channels * 16, base_channels * 16), | ||||||
|  |             C3(base_channels * 16, base_channels * 16, base_depth, shortcut=False), | ||||||
|  |         ) | ||||||
|  |         if pretrained: | ||||||
|  |             url = { | ||||||
|  |                 's' : 'https://github.com/bubbliiiing/yolov5-pytorch/releases/download/v1.0/cspdarknet_s_backbone.pth', | ||||||
|  |                 'm' : 'https://github.com/bubbliiiing/yolov5-pytorch/releases/download/v1.0/cspdarknet_m_backbone.pth', | ||||||
|  |                 'l' : 'https://github.com/bubbliiiing/yolov5-pytorch/releases/download/v1.0/cspdarknet_l_backbone.pth', | ||||||
|  |                 'x' : 'https://github.com/bubbliiiing/yolov5-pytorch/releases/download/v1.0/cspdarknet_x_backbone.pth', | ||||||
|  |             }[phi] | ||||||
|  |             checkpoint = torch.hub.load_state_dict_from_url(url=url, map_location="cpu", model_dir="./model_data") | ||||||
|  |             self.load_state_dict(checkpoint, strict=False) | ||||||
|  |             print("Load weights from ", url.split('/')[-1]) | ||||||
|  |              | ||||||
|  |     def forward(self, x): | ||||||
|  |         x = self.stem(x) | ||||||
|  |         x = self.dark2(x) | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   dark3的输出为80, 80, 256,是一个有效特征层 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         x = self.dark3(x) | ||||||
|  |         feat1 = x | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   dark4的输出为40, 40, 512,是一个有效特征层 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         x = self.dark4(x) | ||||||
|  |         feat2 = x | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   dark5的输出为20, 20, 1024,是一个有效特征层 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         x = self.dark5(x) | ||||||
|  |         feat3 = x | ||||||
|  |         return feat1, feat2, feat3 | ||||||
							
								
								
									
										249
									
								
								nets/ConvNext.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								nets/ConvNext.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | |||||||
|  | # Copyright (c) Meta Platforms, Inc. and affiliates. | ||||||
|  |  | ||||||
|  | # All rights reserved. | ||||||
|  |  | ||||||
|  | # This source code is licensed under the license found in the | ||||||
|  | # LICENSE file in the root directory of this source tree. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import math | ||||||
|  |  | ||||||
|  | import numpy as np | ||||||
|  | import torch | ||||||
|  | import torch.nn as nn | ||||||
|  | import torch.nn.functional as F | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def drop_path(x, drop_prob: float = 0., training: bool = False, scale_by_keep: bool = True): | ||||||
|  |     if drop_prob == 0. or not training: | ||||||
|  |         return x | ||||||
|  |     keep_prob       = 1 - drop_prob | ||||||
|  |     shape           = (x.shape[0],) + (1,) * (x.ndim - 1) | ||||||
|  |     random_tensor   = x.new_empty(shape).bernoulli_(keep_prob) | ||||||
|  |     if keep_prob > 0.0 and scale_by_keep: | ||||||
|  |         random_tensor.div_(keep_prob) | ||||||
|  |     return x * random_tensor | ||||||
|  |  | ||||||
|  | class DropPath(nn.Module): | ||||||
|  |     """ | ||||||
|  |     Drop paths (Stochastic Depth) per sample  (when applied in main path of residual blocks). | ||||||
|  |     """ | ||||||
|  |     def __init__(self, drop_prob=None, scale_by_keep=True): | ||||||
|  |         super(DropPath, self).__init__() | ||||||
|  |         self.drop_prob = drop_prob | ||||||
|  |         self.scale_by_keep = scale_by_keep | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         return drop_path(x, self.drop_prob, self.training, self.scale_by_keep) | ||||||
|  |  | ||||||
|  | def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.): | ||||||
|  |     def _no_grad_trunc_normal_(tensor, mean, std, a, b): | ||||||
|  |         def norm_cdf(x): | ||||||
|  |             return (1. + math.erf(x / math.sqrt(2.))) / 2. | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             l = norm_cdf((a - mean) / std) | ||||||
|  |             u = norm_cdf((b - mean) / std) | ||||||
|  |  | ||||||
|  |             tensor.uniform_(2 * l - 1, 2 * u - 1) | ||||||
|  |             tensor.erfinv_() | ||||||
|  |  | ||||||
|  |             tensor.mul_(std * math.sqrt(2.)) | ||||||
|  |             tensor.add_(mean) | ||||||
|  |  | ||||||
|  |             tensor.clamp_(min=a, max=b) | ||||||
|  |             return tensor | ||||||
|  |     return _no_grad_trunc_normal_(tensor, mean, std, a, b) | ||||||
|  |  | ||||||
|  | #--------------------------------------# | ||||||
|  | #   Gelu激活函数的实现 | ||||||
|  | #   利用近似的数学公式 | ||||||
|  | #--------------------------------------# | ||||||
|  | class GELU(nn.Module): | ||||||
|  |     def __init__(self): | ||||||
|  |         super(GELU, self).__init__() | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         return 0.5 * x * (1 + torch.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * torch.pow(x,3)))) | ||||||
|  |      | ||||||
|  | #---------------------------------------------------------------------------------# | ||||||
|  | #   LayerNorm 支持两种形式channels_last (default) or channels_first.  | ||||||
|  | #   channels_last   对应具有形状的输入(batch_size, height, width, channels)  | ||||||
|  | #   channels_first  对应具有形状的输入(batch_size, channels, height, width).    | ||||||
|  | #---------------------------------------------------------------------------------# | ||||||
|  | class LayerNorm(nn.Module): | ||||||
|  |     def __init__(self, normalized_shape, eps=1e-6, data_format="channels_last"): | ||||||
|  |         super().__init__() | ||||||
|  |         self.weight = nn.Parameter(torch.ones(normalized_shape)) | ||||||
|  |         self.bias   = nn.Parameter(torch.zeros(normalized_shape)) | ||||||
|  |         self.eps = eps | ||||||
|  |         self.data_format = data_format | ||||||
|  |         if self.data_format not in ["channels_last", "channels_first"]: | ||||||
|  |             raise NotImplementedError  | ||||||
|  |         self.normalized_shape = (normalized_shape, ) | ||||||
|  |      | ||||||
|  |     def forward(self, x): | ||||||
|  |         if self.data_format == "channels_last": | ||||||
|  |             return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps) | ||||||
|  |         elif self.data_format == "channels_first": | ||||||
|  |             u = x.mean(1, keepdim=True) | ||||||
|  |             s = (x - u).pow(2).mean(1, keepdim=True) | ||||||
|  |             x = (x - u) / torch.sqrt(s + self.eps) | ||||||
|  |             x = self.weight[:, None, None] * x + self.bias[:, None, None] | ||||||
|  |             return x | ||||||
|  |  | ||||||
|  | #--------------------------------------------------------------------------------------------------------------# | ||||||
|  | #   ConvNeXt Block有两种等效的实现: | ||||||
|  | #   (1) DwConv -> LayerNorm (channels_first) -> 1x1 Conv -> GELU -> 1x1 Conv; all in (N, C, H, W) | ||||||
|  | #   (2) DwConv -> Permute to (N, H, W, C); LayerNorm (channels_last) -> Linear -> GELU -> Linear; Permute back | ||||||
|  | #   代码中使用(2),因为这个在PyTorch中稍微快一点 | ||||||
|  | #--------------------------------------------------------------------------------------------------------------# | ||||||
|  | class Block(nn.Module): | ||||||
|  |     def __init__(self, dim, drop_path=0., layer_scale_init_value=1e-6): | ||||||
|  |         super().__init__() | ||||||
|  |         #--------------------------# | ||||||
|  |         #   7x7的逐层卷积 | ||||||
|  |         #--------------------------# | ||||||
|  |         self.dwconv     = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim) | ||||||
|  |         self.norm       = LayerNorm(dim, eps=1e-6) | ||||||
|  |         #--------------------------# | ||||||
|  |         #   利用全连接层代替1x1卷积 | ||||||
|  |         #--------------------------# | ||||||
|  |         self.pwconv1    = nn.Linear(dim, 4 * dim) | ||||||
|  |         self.act        = GELU() | ||||||
|  |         #--------------------------# | ||||||
|  |         #   利用全连接层代替1x1卷积 | ||||||
|  |         #--------------------------# | ||||||
|  |         self.pwconv2    = nn.Linear(4 * dim, dim) | ||||||
|  |         #--------------------------# | ||||||
|  |         #   加入缩放系数 | ||||||
|  |         #--------------------------# | ||||||
|  |         self.gamma      = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True) if layer_scale_init_value > 0 else None | ||||||
|  |         #--------------------------# | ||||||
|  |         #   加入Drop_path正则化 | ||||||
|  |         #--------------------------# | ||||||
|  |         self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         input = x | ||||||
|  |         #--------------------------# | ||||||
|  |         #   7x7的逐层卷积 | ||||||
|  |         #--------------------------# | ||||||
|  |         x = self.dwconv(x) | ||||||
|  |         x = x.permute(0, 2, 3, 1) # (N, C, H, W) -> (N, H, W, C) | ||||||
|  |         x = self.norm(x) | ||||||
|  |         #--------------------------# | ||||||
|  |         #   利用全连接层代替1x1卷积 | ||||||
|  |         #--------------------------# | ||||||
|  |         x = self.pwconv1(x) | ||||||
|  |         x = self.act(x) | ||||||
|  |         #--------------------------# | ||||||
|  |         #   利用全连接层代替1x1卷积 | ||||||
|  |         #--------------------------# | ||||||
|  |         x = self.pwconv2(x) | ||||||
|  |         #--------------------------# | ||||||
|  |         #   加入缩放系数 | ||||||
|  |         #--------------------------# | ||||||
|  |         if self.gamma is not None: | ||||||
|  |             x = self.gamma * x | ||||||
|  |         x = x.permute(0, 3, 1, 2) # (N, H, W, C) -> (N, C, H, W) | ||||||
|  |         #--------------------------# | ||||||
|  |         #   加入Drop_path正则化 | ||||||
|  |         #--------------------------# | ||||||
|  |         x = input + self.drop_path(x) | ||||||
|  |         return x | ||||||
|  |  | ||||||
|  | #-----------------------------------------------------# | ||||||
|  | #   ConvNeXt | ||||||
|  | #   A PyTorch impl of : `A ConvNet for the 2020s` | ||||||
|  | #   https://arxiv.org/pdf/2201.03545.pdf | ||||||
|  | #-----------------------------------------------------# | ||||||
|  | class ConvNeXt(nn.Module): | ||||||
|  |     def __init__( | ||||||
|  |         self, in_chans=3, num_classes=1000, depths=[3, 3, 9, 3], dims=[96, 192, 384, 768],  | ||||||
|  |         drop_path_rate=0., layer_scale_init_value=1e-6, head_init_scale=1., **kwargs | ||||||
|  |     ): | ||||||
|  |         super().__init__() | ||||||
|  |  | ||||||
|  |         self.downsample_layers = nn.ModuleList() | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         #   bs, 3, 224, 224 -> bs, 96, 56, 56 | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         stem = nn.Sequential( | ||||||
|  |             nn.Conv2d(in_chans, dims[0], kernel_size=4, stride=4), | ||||||
|  |             LayerNorm(dims[0], eps=1e-6, data_format="channels_first") | ||||||
|  |         ) | ||||||
|  |         self.downsample_layers.append(stem) | ||||||
|  |          | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         #   定义三次下采样的过程 | ||||||
|  |         #   利用步长为2x2,卷积核大小为2x2的卷积进行下采样 | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         for i in range(3): | ||||||
|  |             downsample_layer = nn.Sequential( | ||||||
|  |                 LayerNorm(dims[i], eps=1e-6, data_format="channels_first"), | ||||||
|  |                 nn.Conv2d(dims[i], dims[i+1], kernel_size=2, stride=2), | ||||||
|  |             ) | ||||||
|  |             self.downsample_layers.append(downsample_layer) | ||||||
|  |  | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         #   根据深度的不同,定义不同的drop率 | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         self.stages = nn.ModuleList() | ||||||
|  |         dp_rates    = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))]  | ||||||
|  |         cur         = 0 | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         #   整个ConvNeXt除了Stem外,存在四个Stage | ||||||
|  |         #   每个Stage里面是多个ConvNeXt Block的堆叠。 | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         for i in range(4): | ||||||
|  |             stage = nn.Sequential( | ||||||
|  |                 *[Block(dim=dims[i], drop_path=dp_rates[cur + j], layer_scale_init_value=layer_scale_init_value) for j in range(depths[i])] | ||||||
|  |             ) | ||||||
|  |             self.stages.append(stage) | ||||||
|  |             cur += depths[i] | ||||||
|  |  | ||||||
|  |         self.apply(self._init_weights) | ||||||
|  |  | ||||||
|  |     def _init_weights(self, m): | ||||||
|  |         if isinstance(m, (nn.Conv2d, nn.Linear)): | ||||||
|  |             trunc_normal_(m.weight, std=.02) | ||||||
|  |             nn.init.constant_(m.bias, 0) | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         outs = [] | ||||||
|  |         for i in range(4): | ||||||
|  |             x = self.downsample_layers[i](x) | ||||||
|  |             x = self.stages[i](x) | ||||||
|  |             if i != 0: | ||||||
|  |                 outs.append(x) | ||||||
|  |         return outs | ||||||
|  |  | ||||||
|  | model_urls = { | ||||||
|  |     "convnext_tiny_1k"      : "https://github.com/bubbliiiing/yolov5-pytorch/releases/download/v1.0/convnext_tiny_1k_224_ema_no_jit.pth", | ||||||
|  |     "convnext_small_1k"     : "https://github.com/bubbliiiing/yolov5-pytorch/releases/download/v1.0/convnext_small_1k_224_ema_no_jit.pth", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #------------------------------------------------------# | ||||||
|  | #   Tiny约等于Cspdarknet-L的尺寸 | ||||||
|  | #------------------------------------------------------# | ||||||
|  | def ConvNeXt_Tiny(pretrained=False, **kwargs): | ||||||
|  |     model = ConvNeXt(depths=[3, 3, 9, 3], dims=[96, 192, 384, 768], **kwargs) | ||||||
|  |     if pretrained: | ||||||
|  |         url = model_urls['convnext_tiny_1k'] | ||||||
|  |         checkpoint = torch.hub.load_state_dict_from_url(url=url, map_location="cpu", model_dir="./model_data") | ||||||
|  |         model.load_state_dict(checkpoint, strict=False) | ||||||
|  |         print("Load weights from ", url.split('/')[-1]) | ||||||
|  |     return model | ||||||
|  |  | ||||||
|  | #------------------------------------------------------# | ||||||
|  | #   Tiny约等于Cspdarknet-X的尺寸 | ||||||
|  | #------------------------------------------------------# | ||||||
|  | def ConvNeXt_Small(pretrained=False, **kwargs): | ||||||
|  |     model = ConvNeXt(depths=[3, 3, 27, 3], dims=[96, 192, 384, 768], **kwargs) | ||||||
|  |     if pretrained: | ||||||
|  |         url = model_urls['convnext_small_1k'] | ||||||
|  |         checkpoint = torch.hub.load_state_dict_from_url(url=url, map_location="cpu", model_dir="./model_data") | ||||||
|  |         model.load_state_dict(checkpoint, strict=False) | ||||||
|  |         print("Load weights from ", url.split('/')[-1]) | ||||||
|  |     return model | ||||||
							
								
								
									
										638
									
								
								nets/Swin_transformer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										638
									
								
								nets/Swin_transformer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,638 @@ | |||||||
|  | # -------------------------------------------------------- | ||||||
|  | # Swin Transformer | ||||||
|  | # Copyright (c) 2021 Microsoft | ||||||
|  | # Licensed under The MIT License [see LICENSE for details] | ||||||
|  | # Written by Ze Liu | ||||||
|  | # -------------------------------------------------------- | ||||||
|  | import math | ||||||
|  |  | ||||||
|  | import numpy as np | ||||||
|  | import torch | ||||||
|  | import torch.nn as nn | ||||||
|  | import torch.nn.functional as F | ||||||
|  | import torch.utils.checkpoint as checkpoint | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _make_divisible(v, divisor, min_value=None): | ||||||
|  |     if min_value is None: | ||||||
|  |         min_value = divisor | ||||||
|  |     new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) | ||||||
|  |     if new_v < 0.9 * v: | ||||||
|  |         new_v += divisor | ||||||
|  |     return new_v | ||||||
|  |  | ||||||
|  | def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.): | ||||||
|  |     def _no_grad_trunc_normal_(tensor, mean, std, a, b): | ||||||
|  |         def norm_cdf(x): | ||||||
|  |             return (1. + math.erf(x / math.sqrt(2.))) / 2. | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             l = norm_cdf((a - mean) / std) | ||||||
|  |             u = norm_cdf((b - mean) / std) | ||||||
|  |  | ||||||
|  |             tensor.uniform_(2 * l - 1, 2 * u - 1) | ||||||
|  |             tensor.erfinv_() | ||||||
|  |  | ||||||
|  |             tensor.mul_(std * math.sqrt(2.)) | ||||||
|  |             tensor.add_(mean) | ||||||
|  |  | ||||||
|  |             tensor.clamp_(min=a, max=b) | ||||||
|  |             return tensor | ||||||
|  |     return _no_grad_trunc_normal_(tensor, mean, std, a, b) | ||||||
|  |  | ||||||
|  | #--------------------------------------# | ||||||
|  | #   Gelu激活函数的实现 | ||||||
|  | #   利用近似的数学公式 | ||||||
|  | #--------------------------------------# | ||||||
|  | class GELU(nn.Module): | ||||||
|  |     def __init__(self): | ||||||
|  |         super(GELU, self).__init__() | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         return 0.5 * x * (1 + torch.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * torch.pow(x,3)))) | ||||||
|  |  | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | #   对输入进来的图片进行高和宽的压缩 | ||||||
|  | #   并且进行通道的扩张。 | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | class PatchEmbed(nn.Module): | ||||||
|  |     def __init__(self, img_size=[224, 224], patch_size=4, in_chans=3, embed_dim=96, norm_layer=None): | ||||||
|  |         super().__init__() | ||||||
|  |         # [224, 224] | ||||||
|  |         self.img_size           = img_size | ||||||
|  |         # [4, 4] | ||||||
|  |         self.patch_size         = [patch_size, patch_size] | ||||||
|  |         # [56, 56] | ||||||
|  |         self.patches_resolution = [self.img_size[0] // self.patch_size[0], self.img_size[1] // self.patch_size[1]] | ||||||
|  |  | ||||||
|  |         # 3136 | ||||||
|  |         self.num_patches        = self.patches_resolution[0] * self.patches_resolution[1] | ||||||
|  |         # 3 | ||||||
|  |         self.in_chans           = in_chans | ||||||
|  |         # 96 | ||||||
|  |         self.embed_dim          = embed_dim | ||||||
|  |  | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   bs, 224, 224, 3 -> bs, 56, 56, 96 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) | ||||||
|  |         if norm_layer is not None: | ||||||
|  |             self.norm = norm_layer(embed_dim) | ||||||
|  |         else: | ||||||
|  |             self.norm = None | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         B, C, H, W = x.shape | ||||||
|  |         # FIXME look at relaxing size constraints | ||||||
|  |         assert H == self.img_size[0] and W == self.img_size[1], \ | ||||||
|  |             f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]} * {self.img_size[1]})." | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   bs, 224, 224, 3 -> bs, 56, 56, 96 -> bs, 3136, 96 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         x = self.proj(x).flatten(2).transpose(1, 2) | ||||||
|  |         if self.norm is not None: | ||||||
|  |             x = self.norm(x) | ||||||
|  |         return x | ||||||
|  |  | ||||||
|  | def window_partition(x, window_size): | ||||||
|  |     B, H, W, C  = x.shape | ||||||
|  |     #------------------------------------------------------------------# | ||||||
|  |     #   bs, 56, 56, 96 -> bs, 8, 7, 8, 7, 96 -> bs * 64, 7, 7, 96 | ||||||
|  |     #------------------------------------------------------------------# | ||||||
|  |     x           = x.view(B, H // window_size, window_size, W // window_size, window_size, C) | ||||||
|  |     windows     = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) | ||||||
|  |     return windows | ||||||
|  |  | ||||||
|  | def window_reverse(windows, window_size, H, W): | ||||||
|  |     #------------------------------------------------------------------# | ||||||
|  |     #   bs * 64, 7, 7, 96 -> bs, 8, 8, 7, 7, 96 -> bs, 56, 56, 96 | ||||||
|  |     #------------------------------------------------------------------# | ||||||
|  |     B = int(windows.shape[0] / (H * W / window_size / window_size)) | ||||||
|  |     x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) | ||||||
|  |     x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) | ||||||
|  |     return x | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class WindowAttention(nn.Module): | ||||||
|  |     def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.): | ||||||
|  |         super().__init__() | ||||||
|  |         self.dim            = dim | ||||||
|  |         self.window_size    = window_size  # Wh, Ww | ||||||
|  |         self.num_heads      = num_heads | ||||||
|  |         head_dim            = dim // num_heads | ||||||
|  |         self.scale          = qk_scale or head_dim ** -0.5 | ||||||
|  |  | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   相对坐标矩阵,用于表示每个窗口内,其它点相对于自己的坐标 | ||||||
|  |         #   由于相对坐标取值范围为-6 ~ +6。中间共13个值,因此需要13 * 13 | ||||||
|  |         #   13 * 13, num_heads | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         self.relative_position_bias_table = nn.Parameter( | ||||||
|  |             torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads) | ||||||
|  |         )  | ||||||
|  |          | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   该部分用于获取7x7的矩阵内部,其它特征点相对于自身相对坐标 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         coords_h    = torch.arange(self.window_size[0]) | ||||||
|  |         coords_w    = torch.arange(self.window_size[1]) | ||||||
|  |         coords      = torch.stack(torch.meshgrid([coords_h, coords_w]))  # 2, Wh, Ww | ||||||
|  |         coords_flatten  = torch.flatten(coords, 1)  # 2, Wh*Ww | ||||||
|  |         relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :]  # 2, Wh*Ww, Wh*Ww | ||||||
|  |         relative_coords = relative_coords.permute(1, 2, 0).contiguous()  # Wh*Ww, Wh*Ww, 2 | ||||||
|  |         relative_coords[:, :, 0]    += self.window_size[0] - 1  # shift to start from 0 | ||||||
|  |         relative_coords[:, :, 1]    += self.window_size[1] - 1 | ||||||
|  |         relative_coords[:, :, 0]    *= 2 * self.window_size[1] - 1 | ||||||
|  |         relative_position_index     = relative_coords.sum(-1)  # Wh*Ww, Wh*Ww | ||||||
|  |         self.register_buffer("relative_position_index", relative_position_index) | ||||||
|  |  | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   乘积获得q、k、v,用于计算多头注意力机制 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         self.qkv        = nn.Linear(dim, dim * 3, bias=qkv_bias) | ||||||
|  |         self.attn_drop  = nn.Dropout(attn_drop) | ||||||
|  |         self.proj       = nn.Linear(dim, dim) | ||||||
|  |         self.proj_drop  = nn.Dropout(proj_drop) | ||||||
|  |  | ||||||
|  |         trunc_normal_(self.relative_position_bias_table, std=.02) | ||||||
|  |         self.softmax = nn.Softmax(dim=-1) | ||||||
|  |  | ||||||
|  |     def forward(self, x, mask=None): | ||||||
|  |         B_, N, C    = x.shape | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   bs * 64, 49, 96 -> bs * 64, 49, 96 * 3 ->  | ||||||
|  |         #   bs * 64, 49, 3, num_heads, 32 -> 3, bs * 64, num_head, 49, 32     | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         qkv         = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   bs * 64, num_head, 49, 32    | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         q, k, v     = qkv[0], qkv[1], qkv[2]  | ||||||
|  |  | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   bs * 64, num_head, 49, 49 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         q       = q * self.scale | ||||||
|  |         attn    = (q @ k.transpose(-2, -1)) | ||||||
|  |  | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   这一步是根据已经求得的注意力,加上相对坐标的偏执量 | ||||||
|  |         #   形成最后的注意力 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view( | ||||||
|  |             self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1)  # Wh*Ww,Wh*Ww,nH | ||||||
|  |         relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() | ||||||
|  |         attn = attn + relative_position_bias.unsqueeze(0) | ||||||
|  |  | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   加上mask,保证分区。 | ||||||
|  |         #   bs * 64, num_head, 49, 49 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         if mask is not None: | ||||||
|  |             nW = mask.shape[0] | ||||||
|  |             attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0) | ||||||
|  |             attn = attn.view(-1, self.num_heads, N, N) | ||||||
|  |             attn = self.softmax(attn) | ||||||
|  |         else: | ||||||
|  |             attn = self.softmax(attn) | ||||||
|  |  | ||||||
|  |         attn = self.attn_drop(attn) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------------------------------------------# | ||||||
|  |         #   bs * 64, num_head, 49, 49 @ bs * 64, num_head, 49, 32 -> bs * 64, num_head, 49, 32 | ||||||
|  |         #     | ||||||
|  |         #   bs * 64, num_head, 49, 32 -> bs * 64, 49, 96 | ||||||
|  |         #---------------------------------------------------------------------------------------# | ||||||
|  |         x = (attn @ v).transpose(1, 2).reshape(B_, N, C) | ||||||
|  |         x = self.proj(x) | ||||||
|  |         x = self.proj_drop(x) | ||||||
|  |         return x | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def drop_path(x, drop_prob: float = 0., training: bool = False, scale_by_keep: bool = True): | ||||||
|  |     """ | ||||||
|  |     Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks). | ||||||
|  |     This is the same as the DropConnect impl I created for EfficientNet, etc networks, however, | ||||||
|  |     the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper... | ||||||
|  |     See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for | ||||||
|  |     changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use | ||||||
|  |     'survival rate' as the argument. | ||||||
|  |     """ | ||||||
|  |     if drop_prob == 0. or not training: | ||||||
|  |         return x | ||||||
|  |     keep_prob       = 1 - drop_prob | ||||||
|  |     shape           = (x.shape[0],) + (1,) * (x.ndim - 1)  # work with diff dim tensors, not just 2D ConvNets | ||||||
|  |     random_tensor   = x.new_empty(shape).bernoulli_(keep_prob) | ||||||
|  |     if keep_prob > 0.0 and scale_by_keep: | ||||||
|  |         random_tensor.div_(keep_prob) | ||||||
|  |     return x * random_tensor | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class DropPath(nn.Module): | ||||||
|  |     """ | ||||||
|  |     Drop paths (Stochastic Depth) per sample  (when applied in main path of residual blocks). | ||||||
|  |     """ | ||||||
|  |     def __init__(self, drop_prob=None, scale_by_keep=True): | ||||||
|  |         super(DropPath, self).__init__() | ||||||
|  |         self.drop_prob = drop_prob | ||||||
|  |         self.scale_by_keep = scale_by_keep | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         return drop_path(x, self.drop_prob, self.training, self.scale_by_keep) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | #   两次全连接 | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | class Mlp(nn.Module): | ||||||
|  |     def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=GELU, drop=0.): | ||||||
|  |         super().__init__() | ||||||
|  |         out_features = out_features or in_features | ||||||
|  |         hidden_features = hidden_features or in_features | ||||||
|  |         self.fc1 = nn.Linear(in_features, hidden_features) | ||||||
|  |         self.act = act_layer() | ||||||
|  |         self.fc2 = nn.Linear(hidden_features, out_features) | ||||||
|  |         self.drop = nn.Dropout(drop) | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         x = self.fc1(x) | ||||||
|  |         x = self.act(x) | ||||||
|  |         x = self.drop(x) | ||||||
|  |         x = self.fc2(x) | ||||||
|  |         x = self.drop(x) | ||||||
|  |         return x | ||||||
|  |  | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | #   每个阶段重复的基础模块 | ||||||
|  | #   在这其中会使用WindowAttention进行特征提取 | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | class SwinTransformerBlock(nn.Module): | ||||||
|  |     def __init__(self, dim, input_resolution, num_heads, window_size=7, shift_size=0, | ||||||
|  |                  mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0., | ||||||
|  |                  act_layer=GELU, norm_layer=nn.LayerNorm): | ||||||
|  |         super().__init__() | ||||||
|  |         self.dim                = dim | ||||||
|  |         self.input_resolution   = input_resolution | ||||||
|  |         self.num_heads          = num_heads | ||||||
|  |         self.window_size        = window_size | ||||||
|  |         self.shift_size         = shift_size | ||||||
|  |  | ||||||
|  |         self.mlp_ratio          = mlp_ratio | ||||||
|  |         if min(self.input_resolution) <= self.window_size: | ||||||
|  |             self.shift_size = 0 | ||||||
|  |             self.window_size = min(self.input_resolution) | ||||||
|  |         assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size" | ||||||
|  |  | ||||||
|  |         self.norm1  = norm_layer(dim) | ||||||
|  |         self.attn   = WindowAttention( | ||||||
|  |             dim,  | ||||||
|  |             window_size = [self.window_size, self.window_size],  | ||||||
|  |             num_heads   = num_heads, | ||||||
|  |             qkv_bias    = qkv_bias,  | ||||||
|  |             qk_scale    = qk_scale,  | ||||||
|  |             attn_drop   = attn_drop,  | ||||||
|  |             proj_drop   = drop | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self.drop_path  = DropPath(drop_path) if drop_path > 0. else nn.Identity() | ||||||
|  |         self.norm2      = norm_layer(dim) | ||||||
|  |         mlp_hidden_dim  = int(dim * mlp_ratio) | ||||||
|  |         self.mlp        = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) | ||||||
|  |  | ||||||
|  |         if self.shift_size > 0: | ||||||
|  |             #----------------------------------------------------------------# | ||||||
|  |             #   由于进行特征提取时,会对输入的特征层进行的平移 | ||||||
|  |             #   如: | ||||||
|  |             #   [                                   [ | ||||||
|  |             #       [1, 2, 3],                          [5, 6, 4],    | ||||||
|  |             #       [4, 5, 6],          -->             [8, 9, 7], | ||||||
|  |             #       [7, 8, 9],                          [1, 2, 3], | ||||||
|  |             #   ]                                   ] | ||||||
|  |             #   这一步的作用就是使得平移后的区域块只计算自己部分的注意力机制 | ||||||
|  |             #----------------------------------------------------------------# | ||||||
|  |             H, W = self.input_resolution | ||||||
|  |             _H, _W  =  _make_divisible(H, self.window_size), _make_divisible(W, self.window_size), | ||||||
|  |             img_mask = torch.zeros((1, _H, _W, 1))  # 1 H W 1 | ||||||
|  |             h_slices = (slice(0, -self.window_size), | ||||||
|  |                         slice(-self.window_size, -self.shift_size), | ||||||
|  |                         slice(-self.shift_size, None)) | ||||||
|  |             w_slices = (slice(0, -self.window_size), | ||||||
|  |                         slice(-self.window_size, -self.shift_size), | ||||||
|  |                         slice(-self.shift_size, None)) | ||||||
|  |             cnt = 0 | ||||||
|  |             for h in h_slices: | ||||||
|  |                 for w in w_slices: | ||||||
|  |                     img_mask[:, h, w, :] = cnt | ||||||
|  |                     cnt += 1 | ||||||
|  |  | ||||||
|  |             mask_windows = window_partition(img_mask, self.window_size)  # nW, window_size, window_size, 1 | ||||||
|  |             mask_windows = mask_windows.view(-1, self.window_size * self.window_size) | ||||||
|  |             attn_mask       = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) | ||||||
|  |             attn_mask       = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0)) | ||||||
|  |             self.attn_mask  = attn_mask.cpu().numpy() | ||||||
|  |         else: | ||||||
|  |             self.attn_mask = None | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         H, W = self.input_resolution | ||||||
|  |         B, L, C = x.shape | ||||||
|  |         assert L == H * W, "input feature has wrong size" | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   bs, 3136, 96 -> bs, 56, 56, 96 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         shortcut = x | ||||||
|  |         x = self.norm1(x) | ||||||
|  |         x = x.view(B, H, W, C) | ||||||
|  |  | ||||||
|  |         _H, _W  =  _make_divisible(H, self.window_size), _make_divisible(W, self.window_size), | ||||||
|  |         x       = x.permute(0, 3, 1, 2) | ||||||
|  |         x       = F.interpolate(x, [_H, _W], mode='bicubic', align_corners=False).permute(0, 2, 3, 1) | ||||||
|  |  | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   进行特征层的平移 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         if self.shift_size > 0: | ||||||
|  |             shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2)) | ||||||
|  |         else: | ||||||
|  |             shifted_x = x | ||||||
|  |         #------------------------------------------------------------------------------------------# | ||||||
|  |         #   bs, 56, 56, 96 -> bs * 64, 7, 7, 96 -> bs * 64, 49, 96 | ||||||
|  |         #------------------------------------------------------------------------------------------# | ||||||
|  |         x_windows = window_partition(shifted_x, self.window_size)  # num_windows * B, window_size, window_size, C | ||||||
|  |         x_windows = x_windows.view(-1, self.window_size * self.window_size, C)  # nW*B, window_size*window_size, C | ||||||
|  |  | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   bs * 64, 49, 97 -> bs * 64, 49, 97 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         if type(self.attn_mask) != type(None): | ||||||
|  |             attn_mask = torch.tensor(self.attn_mask).cuda() if x.is_cuda else torch.tensor(self.attn_mask) | ||||||
|  |         else: | ||||||
|  |             attn_mask = None | ||||||
|  |         attn_windows = self.attn(x_windows, mask=attn_mask)  # nW*B, window_size*window_size, C | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   bs * 64, 49, 97 -> bs, 56, 56, 96 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C) | ||||||
|  |         shifted_x = window_reverse(attn_windows, self.window_size, _H, _W)  # B H' W' C | ||||||
|  |  | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   将特征层平移回来 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         if self.shift_size > 0: | ||||||
|  |             x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2)) | ||||||
|  |         else: | ||||||
|  |             x = shifted_x | ||||||
|  |          | ||||||
|  |         x = x.permute(0, 3, 1, 2) | ||||||
|  |         x = F.interpolate(x, [H, W], mode='bicubic', align_corners=False).permute(0, 2, 3, 1) | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   bs, 3136, 96 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         x = x.view(B, H * W, C) | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   FFN | ||||||
|  |         #   bs, 3136, 96 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         x = shortcut + self.drop_path(x) | ||||||
|  |         x = x + self.drop_path(self.mlp(self.norm2(x))) | ||||||
|  |  | ||||||
|  |         return x | ||||||
|  |  | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | #   对输入进来的特征层进行高和宽的压缩 | ||||||
|  | #   进行跨特征点的特征提取,提取完成后进行堆叠。 | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | class PatchMerging(nn.Module): | ||||||
|  |     def __init__(self, input_resolution, dim, norm_layer=nn.LayerNorm): | ||||||
|  |         super().__init__() | ||||||
|  |         self.input_resolution   = input_resolution | ||||||
|  |         self.dim                = dim | ||||||
|  |  | ||||||
|  |         self.norm               = norm_layer(4 * dim) | ||||||
|  |         self.reduction          = nn.Linear(4 * dim, 2 * dim, bias=False) | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         H, W = self.input_resolution | ||||||
|  |         B, L, C = x.shape | ||||||
|  |         assert L == H * W, "input feature has wrong size" | ||||||
|  |         assert H % 2 == 0 and W % 2 == 0, f"x size ({H}*{W}) are not even." | ||||||
|  |  | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   bs, 3136, 96 -> bs, 56, 56, 96 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         x = x.view(B, H, W, C) | ||||||
|  |  | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   x0 ~ x3   bs, 56, 56, 96 -> bs, 28, 28, 96 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         x0 = x[:, 0::2, 0::2, :]  # B H/2 W/2 C | ||||||
|  |         x1 = x[:, 1::2, 0::2, :]  # B H/2 W/2 C | ||||||
|  |         x2 = x[:, 0::2, 1::2, :]  # B H/2 W/2 C | ||||||
|  |         x3 = x[:, 1::2, 1::2, :]  # B H/2 W/2 C | ||||||
|  |          | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   4 X bs, 28, 28, 96 -> bs, 28, 28, 384 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         x = torch.cat([x0, x1, x2, x3], -1)  # B H/2 W/2 4*C | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   bs, 28, 28, 384 -> bs, 784, 384 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         x = x.view(B, -1, 4 * C)  # B H/2*W/2 4*C | ||||||
|  |  | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   bs, 784, 384 -> bs, 784, 192 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         x = self.norm(x) | ||||||
|  |         x = self.reduction(x) | ||||||
|  |         return x | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | #   Swin-Transformer的基础模块。 | ||||||
|  | #   使用窗口多头注意力机制进行特征提取。 | ||||||
|  | #   使用PatchMerging进行高和宽的压缩。 | ||||||
|  | #-------------------------------------------------------# | ||||||
|  | class BasicLayer(nn.Module): | ||||||
|  |     def __init__(self, dim, input_resolution, depth, num_heads, window_size, | ||||||
|  |                  mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., | ||||||
|  |                  drop_path=0., norm_layer=nn.LayerNorm, downsample=None, use_checkpoint=False): | ||||||
|  |         super().__init__() | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   四个阶段对应不同的dim | ||||||
|  |         #   [96, 192, 384, 768] | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         self.dim                = dim | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   四个阶段对应不同的输入分辨率 | ||||||
|  |         #   [[56, 56], [28, 28], [14, 14], [7, 7]] | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         self.input_resolution   = input_resolution | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   四个阶段对应不同的多头注意力机制重复次数   | ||||||
|  |         #   [2, 2, 6, 2] | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         self.depth              = depth | ||||||
|  |         self.use_checkpoint     = use_checkpoint | ||||||
|  |  | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   根据depth的次数利用窗口多头注意力机制进行特征提取。 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         self.blocks = nn.ModuleList( | ||||||
|  |             [ | ||||||
|  |                 SwinTransformerBlock( | ||||||
|  |                     dim         = dim,  | ||||||
|  |                     input_resolution = input_resolution, | ||||||
|  |                     num_heads   = num_heads,  | ||||||
|  |                     window_size = window_size, | ||||||
|  |                     shift_size  = 0 if (i % 2 == 0) else window_size // 2, | ||||||
|  |                     mlp_ratio   = mlp_ratio, | ||||||
|  |                     qkv_bias    = qkv_bias,  | ||||||
|  |                     qk_scale    = qk_scale, | ||||||
|  |                     drop        = drop,  | ||||||
|  |                     attn_drop   = attn_drop, | ||||||
|  |                     drop_path   = drop_path[i] if isinstance(drop_path, list) else drop_path, | ||||||
|  |                     norm_layer  = norm_layer | ||||||
|  |                 ) | ||||||
|  |                 for i in range(depth) | ||||||
|  |             ] | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         if downsample is not None: | ||||||
|  |             #-------------------------------------------------------# | ||||||
|  |             #   判断是否要进行下采样,即:高宽压缩 | ||||||
|  |             #-------------------------------------------------------# | ||||||
|  |             self.downsample = downsample(input_resolution, dim=dim, norm_layer=norm_layer) | ||||||
|  |         else: | ||||||
|  |             self.downsample = None | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         for blk in self.blocks: | ||||||
|  |             if self.use_checkpoint: | ||||||
|  |                 x_ = checkpoint.checkpoint(blk, x) | ||||||
|  |             else: | ||||||
|  |                 x_ = blk(x) | ||||||
|  |         if self.downsample is not None: | ||||||
|  |             x = self.downsample(x_) | ||||||
|  |         else: | ||||||
|  |             x = x_ | ||||||
|  |         return x_, x | ||||||
|  |  | ||||||
|  | class SwinTransformer(nn.Module): | ||||||
|  |     def __init__(self, img_size=[640, 640], patch_size=4, in_chans=3, num_classes=1000, | ||||||
|  |                  embed_dim=96, depths=[2, 2, 6, 2], num_heads=[3, 6, 12, 24], | ||||||
|  |                  window_size=7, mlp_ratio=4., qkv_bias=True, qk_scale=None, | ||||||
|  |                  drop_rate=0., attn_drop_rate=0., drop_path_rate=0.1, | ||||||
|  |                  norm_layer=nn.LayerNorm, ape=False, patch_norm=True, | ||||||
|  |                  use_checkpoint=False, **kwargs): | ||||||
|  |         super().__init__() | ||||||
|  |         self.num_classes    = num_classes | ||||||
|  |         self.num_layers     = len(depths) | ||||||
|  |         self.embed_dim      = embed_dim | ||||||
|  |         self.ape            = ape | ||||||
|  |         self.patch_norm     = patch_norm | ||||||
|  |         self.num_features   = int(embed_dim * 2 ** (self.num_layers - 1)) | ||||||
|  |         self.mlp_ratio      = mlp_ratio | ||||||
|  |          | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         #   bs, 224, 224, 3 -> bs, 3136, 96 | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         self.patch_embed = PatchEmbed( | ||||||
|  |             img_size    = img_size,  | ||||||
|  |             patch_size  = patch_size, | ||||||
|  |             in_chans    = in_chans,  | ||||||
|  |             embed_dim   = embed_dim, | ||||||
|  |             norm_layer  = norm_layer if self.patch_norm else None | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         #   PatchEmbed之后的图像序列长度        3136 | ||||||
|  |         #   PatchEmbed之后的图像对应的分辨率    [56, 56] | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         num_patches             = self.patch_embed.num_patches | ||||||
|  |         patches_resolution      = self.patch_embed.patches_resolution | ||||||
|  |         self.patches_resolution = patches_resolution | ||||||
|  |  | ||||||
|  |         if self.ape: | ||||||
|  |             self.absolute_pos_embed = nn.Parameter(torch.zeros(1, num_patches, embed_dim)) | ||||||
|  |             trunc_normal_(self.absolute_pos_embed, std=.02) | ||||||
|  |  | ||||||
|  |         self.pos_drop = nn.Dropout(p=drop_rate) | ||||||
|  |  | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         #   stochastic depth | ||||||
|  |         #--------------------------------------------------# | ||||||
|  |         dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))]  # stochastic depth decay rule | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------------------# | ||||||
|  |         #   构建swin-transform的每个阶段 | ||||||
|  |         #   bs, 3136, 96 -> bs, 784, 192 -> bs, 196, 384 -> bs, 49, 768 | ||||||
|  |         #---------------------------------------------------------------# | ||||||
|  |         self.layers = nn.ModuleList() | ||||||
|  |         for i_layer in range(self.num_layers): | ||||||
|  |             layer = BasicLayer( | ||||||
|  |                 dim                 = int(embed_dim * 2 ** i_layer), | ||||||
|  |                 input_resolution    = (patches_resolution[0] // (2 ** i_layer), patches_resolution[1] // (2 ** i_layer)), | ||||||
|  |                 depth               = depths[i_layer], | ||||||
|  |                 num_heads           = num_heads[i_layer], | ||||||
|  |                 window_size         = window_size, | ||||||
|  |                 mlp_ratio           = self.mlp_ratio, | ||||||
|  |                 qkv_bias            = qkv_bias,  | ||||||
|  |                 qk_scale            = qk_scale, | ||||||
|  |                 drop                = drop_rate,  | ||||||
|  |                 attn_drop           = attn_drop_rate, | ||||||
|  |                 drop_path           = dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])], | ||||||
|  |                 norm_layer          = norm_layer, | ||||||
|  |                 downsample          = PatchMerging if (i_layer < self.num_layers - 1) else None, | ||||||
|  |                 use_checkpoint      = use_checkpoint | ||||||
|  |             ) | ||||||
|  |             self.layers.append(layer) | ||||||
|  |  | ||||||
|  |         self.apply(self._init_weights) | ||||||
|  |  | ||||||
|  |     def _init_weights(self, m): | ||||||
|  |         if isinstance(m, nn.Linear): | ||||||
|  |             trunc_normal_(m.weight, std=.02) | ||||||
|  |             if isinstance(m, nn.Linear) and m.bias is not None: | ||||||
|  |                 nn.init.constant_(m.bias, 0) | ||||||
|  |         elif isinstance(m, nn.LayerNorm): | ||||||
|  |             nn.init.constant_(m.bias, 0) | ||||||
|  |             nn.init.constant_(m.weight, 1.0) | ||||||
|  |  | ||||||
|  |     @torch.jit.ignore | ||||||
|  |     def no_weight_decay(self): | ||||||
|  |         return {'absolute_pos_embed'} | ||||||
|  |  | ||||||
|  |     @torch.jit.ignore | ||||||
|  |     def no_weight_decay_keywords(self): | ||||||
|  |         return {'relative_position_bias_table'} | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         x = self.patch_embed(x) | ||||||
|  |         if self.ape: | ||||||
|  |             x = x + self.absolute_pos_embed | ||||||
|  |         x = self.pos_drop(x) | ||||||
|  |  | ||||||
|  |         inverval_outs = [] | ||||||
|  |         for i, layer in enumerate(self.layers): | ||||||
|  |             x_, x = layer(x) | ||||||
|  |             if i != 0: | ||||||
|  |                 inverval_outs.append(x_) | ||||||
|  |          | ||||||
|  |         outs = [] | ||||||
|  |         for i, layer in enumerate(inverval_outs): | ||||||
|  |             H, W    = (self.patches_resolution[0] // (2 ** (i + 1)), self.patches_resolution[1] // (2 ** (i + 1))) | ||||||
|  |             B, L, C = layer.shape | ||||||
|  |             layer   = layer.view([B, H, W, C]).permute([0, 3, 1, 2]) | ||||||
|  |             outs.append(layer) | ||||||
|  |  | ||||||
|  |         return outs | ||||||
|  |      | ||||||
|  | def Swin_transformer_Tiny(pretrained = False, input_shape = [640, 640], **kwargs): | ||||||
|  |     model = SwinTransformer(input_shape, depths=[2, 2, 6, 2], **kwargs) | ||||||
|  |     if pretrained: | ||||||
|  |         url = "https://github.com/bubbliiiing/yolov5-pytorch/releases/download/v1.0/swin_tiny_patch4_window7.pth" | ||||||
|  |         checkpoint = torch.hub.load_state_dict_from_url(url=url, map_location="cpu", model_dir="./model_data") | ||||||
|  |         model.load_state_dict(checkpoint, strict=False) | ||||||
|  |         print("Load weights from ", url.split('/')[-1]) | ||||||
|  |          | ||||||
|  |     return model | ||||||
							
								
								
									
										1
									
								
								nets/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								nets/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | # | ||||||
							
								
								
									
										132
									
								
								nets/yolo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								nets/yolo.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | |||||||
|  | import torch | ||||||
|  | import torch.nn as nn | ||||||
|  |  | ||||||
|  | from nets.ConvNext import ConvNeXt_Small, ConvNeXt_Tiny | ||||||
|  | from nets.CSPdarknet import C3, Conv, CSPDarknet | ||||||
|  | from nets.Swin_transformer import Swin_transformer_Tiny | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #---------------------------------------------------# | ||||||
|  | #   yolo_body | ||||||
|  | #---------------------------------------------------# | ||||||
|  | class YoloBody(nn.Module): | ||||||
|  |     def __init__(self, anchors_mask, num_classes, phi, backbone='cspdarknet', pretrained=False, input_shape=[640, 640]): | ||||||
|  |         super(YoloBody, self).__init__() | ||||||
|  |         depth_dict          = {'s' : 0.33, 'm' : 0.67, 'l' : 1.00, 'x' : 1.33,} | ||||||
|  |         width_dict          = {'s' : 0.50, 'm' : 0.75, 'l' : 1.00, 'x' : 1.25,} | ||||||
|  |         dep_mul, wid_mul    = depth_dict[phi], width_dict[phi] | ||||||
|  |  | ||||||
|  |         base_channels       = int(wid_mul * 64)  # 64 | ||||||
|  |         base_depth          = max(round(dep_mul * 3), 1)  # 3 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   输入图片是640, 640, 3 | ||||||
|  |         #   初始的基本通道是64 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         self.backbone_name  = backbone | ||||||
|  |         if backbone == "cspdarknet": | ||||||
|  |             #---------------------------------------------------#    | ||||||
|  |             #   生成CSPdarknet53的主干模型 | ||||||
|  |             #   获得三个有效特征层,他们的shape分别是: | ||||||
|  |             #   80,80,256 | ||||||
|  |             #   40,40,512 | ||||||
|  |             #   20,20,1024 | ||||||
|  |             #---------------------------------------------------# | ||||||
|  |             self.backbone   = CSPDarknet(base_channels, base_depth, phi, pretrained) | ||||||
|  |         else: | ||||||
|  |             #---------------------------------------------------#    | ||||||
|  |             #   如果输入不为cspdarknet,则调整通道数 | ||||||
|  |             #   使其符合YoloV5的格式 | ||||||
|  |             #---------------------------------------------------# | ||||||
|  |             self.backbone       = { | ||||||
|  |                 'convnext_tiny'         : ConvNeXt_Tiny, | ||||||
|  |                 'convnext_small'        : ConvNeXt_Small, | ||||||
|  |                 'swin_transfomer_tiny'  : Swin_transformer_Tiny, | ||||||
|  |             }[backbone](pretrained=pretrained, input_shape=input_shape) | ||||||
|  |             in_channels         = { | ||||||
|  |                 'convnext_tiny'         : [192, 384, 768], | ||||||
|  |                 'convnext_small'        : [192, 384, 768], | ||||||
|  |                 'swin_transfomer_tiny'  : [192, 384, 768], | ||||||
|  |             }[backbone] | ||||||
|  |             feat1_c, feat2_c, feat3_c = in_channels  | ||||||
|  |             self.conv_1x1_feat1 = Conv(feat1_c, base_channels * 4, 1, 1) | ||||||
|  |             self.conv_1x1_feat2 = Conv(feat2_c, base_channels * 8, 1, 1) | ||||||
|  |             self.conv_1x1_feat3 = Conv(feat3_c, base_channels * 16, 1, 1) | ||||||
|  |              | ||||||
|  |         self.upsample   = nn.Upsample(scale_factor=2, mode="nearest") | ||||||
|  |  | ||||||
|  |         self.conv_for_feat3         = Conv(base_channels * 16, base_channels * 8, 1, 1) | ||||||
|  |         self.conv3_for_upsample1    = C3(base_channels * 16, base_channels * 8, base_depth, shortcut=False) | ||||||
|  |  | ||||||
|  |         self.conv_for_feat2         = Conv(base_channels * 8, base_channels * 4, 1, 1) | ||||||
|  |         self.conv3_for_upsample2    = C3(base_channels * 8, base_channels * 4, base_depth, shortcut=False) | ||||||
|  |  | ||||||
|  |         self.down_sample1           = Conv(base_channels * 4, base_channels * 4, 3, 2) | ||||||
|  |         self.conv3_for_downsample1  = C3(base_channels * 8, base_channels * 8, base_depth, shortcut=False) | ||||||
|  |  | ||||||
|  |         self.down_sample2           = Conv(base_channels * 8, base_channels * 8, 3, 2) | ||||||
|  |         self.conv3_for_downsample2  = C3(base_channels * 16, base_channels * 16, base_depth, shortcut=False) | ||||||
|  |  | ||||||
|  |         # 80, 80, 256 => 80, 80, 3 * (5 + num_classes) => 80, 80, 3 * (4 + 1 + num_classes) | ||||||
|  |         self.yolo_head_P3 = nn.Conv2d(base_channels * 4, len(anchors_mask[2]) * (5 + num_classes), 1) | ||||||
|  |         # 40, 40, 512 => 40, 40, 3 * (5 + num_classes) => 40, 40, 3 * (4 + 1 + num_classes) | ||||||
|  |         self.yolo_head_P4 = nn.Conv2d(base_channels * 8, len(anchors_mask[1]) * (5 + num_classes), 1) | ||||||
|  |         # 20, 20, 1024 => 20, 20, 3 * (5 + num_classes) => 20, 20, 3 * (4 + 1 + num_classes) | ||||||
|  |         self.yolo_head_P5 = nn.Conv2d(base_channels * 16, len(anchors_mask[0]) * (5 + num_classes), 1) | ||||||
|  |  | ||||||
|  |     def forward(self, x): | ||||||
|  |         #  backbone | ||||||
|  |         feat1, feat2, feat3 = self.backbone(x) | ||||||
|  |         if self.backbone_name != "cspdarknet": | ||||||
|  |             feat1 = self.conv_1x1_feat1(feat1) | ||||||
|  |             feat2 = self.conv_1x1_feat2(feat2) | ||||||
|  |             feat3 = self.conv_1x1_feat3(feat3) | ||||||
|  |  | ||||||
|  |         # 20, 20, 1024 -> 20, 20, 512 | ||||||
|  |         P5          = self.conv_for_feat3(feat3) | ||||||
|  |         # 20, 20, 512 -> 40, 40, 512 | ||||||
|  |         P5_upsample = self.upsample(P5) | ||||||
|  |         # 40, 40, 512 -> 40, 40, 1024 | ||||||
|  |         P4          = torch.cat([P5_upsample, feat2], 1) | ||||||
|  |         # 40, 40, 1024 -> 40, 40, 512 | ||||||
|  |         P4          = self.conv3_for_upsample1(P4) | ||||||
|  |  | ||||||
|  |         # 40, 40, 512 -> 40, 40, 256 | ||||||
|  |         P4          = self.conv_for_feat2(P4) | ||||||
|  |         # 40, 40, 256 -> 80, 80, 256 | ||||||
|  |         P4_upsample = self.upsample(P4) | ||||||
|  |         # 80, 80, 256 cat 80, 80, 256 -> 80, 80, 512 | ||||||
|  |         P3          = torch.cat([P4_upsample, feat1], 1) | ||||||
|  |         # 80, 80, 512 -> 80, 80, 256 | ||||||
|  |         P3          = self.conv3_for_upsample2(P3) | ||||||
|  |          | ||||||
|  |         # 80, 80, 256 -> 40, 40, 256 | ||||||
|  |         P3_downsample = self.down_sample1(P3) | ||||||
|  |         # 40, 40, 256 cat 40, 40, 256 -> 40, 40, 512 | ||||||
|  |         P4 = torch.cat([P3_downsample, P4], 1) | ||||||
|  |         # 40, 40, 512 -> 40, 40, 512 | ||||||
|  |         P4 = self.conv3_for_downsample1(P4) | ||||||
|  |  | ||||||
|  |         # 40, 40, 512 -> 20, 20, 512 | ||||||
|  |         P4_downsample = self.down_sample2(P4) | ||||||
|  |         # 20, 20, 512 cat 20, 20, 512 -> 20, 20, 1024 | ||||||
|  |         P5 = torch.cat([P4_downsample, P5], 1) | ||||||
|  |         # 20, 20, 1024 -> 20, 20, 1024 | ||||||
|  |         P5 = self.conv3_for_downsample2(P5) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   第三个特征层 | ||||||
|  |         #   y3=(batch_size,75,80,80) | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         out2 = self.yolo_head_P3(P3) | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   第二个特征层 | ||||||
|  |         #   y2=(batch_size,75,40,40) | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         out1 = self.yolo_head_P4(P4) | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   第一个特征层 | ||||||
|  |         #   y1=(batch_size,75,20,20) | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         out0 = self.yolo_head_P5(P5) | ||||||
|  |         return out0, out1, out2 | ||||||
|  |  | ||||||
							
								
								
									
										465
									
								
								nets/yolo_training.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								nets/yolo_training.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,465 @@ | |||||||
|  | import math | ||||||
|  | from copy import deepcopy | ||||||
|  | from functools import partial | ||||||
|  |  | ||||||
|  | import numpy as np | ||||||
|  | import torch | ||||||
|  | import torch.nn as nn | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class YOLOLoss(nn.Module): | ||||||
|  |     def __init__(self, anchors, num_classes, input_shape, cuda, anchors_mask = [[6,7,8], [3,4,5], [0,1,2]], label_smoothing = 0): | ||||||
|  |         super(YOLOLoss, self).__init__() | ||||||
|  |         #-----------------------------------------------------------# | ||||||
|  |         #   20x20的特征层对应的anchor是[116,90],[156,198],[373,326] | ||||||
|  |         #   40x40的特征层对应的anchor是[30,61],[62,45],[59,119] | ||||||
|  |         #   80x80的特征层对应的anchor是[10,13],[16,30],[33,23] | ||||||
|  |         #-----------------------------------------------------------# | ||||||
|  |         self.anchors        = anchors | ||||||
|  |         self.num_classes    = num_classes | ||||||
|  |         self.bbox_attrs     = 5 + num_classes | ||||||
|  |         self.input_shape    = input_shape | ||||||
|  |         self.anchors_mask   = anchors_mask | ||||||
|  |         self.label_smoothing = label_smoothing | ||||||
|  |  | ||||||
|  |         self.threshold      = 4 | ||||||
|  |  | ||||||
|  |         self.balance        = [0.4, 1.0, 4] | ||||||
|  |         self.box_ratio      = 0.05 | ||||||
|  |         self.obj_ratio      = 1 * (input_shape[0] * input_shape[1]) / (640 ** 2) | ||||||
|  |         self.cls_ratio      = 0.5 * (num_classes / 80) | ||||||
|  |         self.cuda = cuda | ||||||
|  |  | ||||||
|  |     def clip_by_tensor(self, t, t_min, t_max): | ||||||
|  |         t = t.float() | ||||||
|  |         result = (t >= t_min).float() * t + (t < t_min).float() * t_min | ||||||
|  |         result = (result <= t_max).float() * result + (result > t_max).float() * t_max | ||||||
|  |         return result | ||||||
|  |  | ||||||
|  |     def MSELoss(self, pred, target): | ||||||
|  |         return torch.pow(pred - target, 2) | ||||||
|  |  | ||||||
|  |     def BCELoss(self, pred, target): | ||||||
|  |         epsilon = 1e-7 | ||||||
|  |         pred    = self.clip_by_tensor(pred, epsilon, 1.0 - epsilon) | ||||||
|  |         output  = - target * torch.log(pred) - (1.0 - target) * torch.log(1.0 - pred) | ||||||
|  |         return output | ||||||
|  |          | ||||||
|  |     def box_giou(self, b1, b2): | ||||||
|  |         """ | ||||||
|  |         输入为: | ||||||
|  |         ---------- | ||||||
|  |         b1: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh | ||||||
|  |         b2: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh | ||||||
|  |  | ||||||
|  |         返回为: | ||||||
|  |         ------- | ||||||
|  |         giou: tensor, shape=(batch, feat_w, feat_h, anchor_num, 1) | ||||||
|  |         """ | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         #   求出预测框左上角右下角 | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         b1_xy       = b1[..., :2] | ||||||
|  |         b1_wh       = b1[..., 2:4] | ||||||
|  |         b1_wh_half  = b1_wh/2. | ||||||
|  |         b1_mins     = b1_xy - b1_wh_half | ||||||
|  |         b1_maxes    = b1_xy + b1_wh_half | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         #   求出真实框左上角右下角 | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         b2_xy       = b2[..., :2] | ||||||
|  |         b2_wh       = b2[..., 2:4] | ||||||
|  |         b2_wh_half  = b2_wh/2. | ||||||
|  |         b2_mins     = b2_xy - b2_wh_half | ||||||
|  |         b2_maxes    = b2_xy + b2_wh_half | ||||||
|  |  | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         #   求真实框和预测框所有的iou | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         intersect_mins  = torch.max(b1_mins, b2_mins) | ||||||
|  |         intersect_maxes = torch.min(b1_maxes, b2_maxes) | ||||||
|  |         intersect_wh    = torch.max(intersect_maxes - intersect_mins, torch.zeros_like(intersect_maxes)) | ||||||
|  |         intersect_area  = intersect_wh[..., 0] * intersect_wh[..., 1] | ||||||
|  |         b1_area         = b1_wh[..., 0] * b1_wh[..., 1] | ||||||
|  |         b2_area         = b2_wh[..., 0] * b2_wh[..., 1] | ||||||
|  |         union_area      = b1_area + b2_area - intersect_area | ||||||
|  |         iou             = intersect_area / union_area | ||||||
|  |  | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         #   找到包裹两个框的最小框的左上角和右下角 | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         enclose_mins    = torch.min(b1_mins, b2_mins) | ||||||
|  |         enclose_maxes   = torch.max(b1_maxes, b2_maxes) | ||||||
|  |         enclose_wh      = torch.max(enclose_maxes - enclose_mins, torch.zeros_like(intersect_maxes)) | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         #   计算对角线距离 | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         enclose_area    = enclose_wh[..., 0] * enclose_wh[..., 1] | ||||||
|  |         giou            = iou - (enclose_area - union_area) / enclose_area | ||||||
|  |          | ||||||
|  |         return giou | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   平滑标签 | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def smooth_labels(self, y_true, label_smoothing, num_classes): | ||||||
|  |         return y_true * (1.0 - label_smoothing) + label_smoothing / num_classes | ||||||
|  |  | ||||||
|  |     def forward(self, l, input, targets=None, y_true=None): | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         #   l               代表使用的是第几个有效特征层 | ||||||
|  |         #   input的shape为  bs, 3*(5+num_classes), 20, 20 | ||||||
|  |         #                   bs, 3*(5+num_classes), 40, 40 | ||||||
|  |         #                   bs, 3*(5+num_classes), 80, 80 | ||||||
|  |         #   targets         真实框的标签情况 [batch_size, num_gt, 5] | ||||||
|  |         #----------------------------------------------------# | ||||||
|  |         #--------------------------------# | ||||||
|  |         #   获得图片数量,特征层的高和宽 | ||||||
|  |         #   20, 20 | ||||||
|  |         #--------------------------------# | ||||||
|  |         bs      = input.size(0) | ||||||
|  |         in_h    = input.size(2) | ||||||
|  |         in_w    = input.size(3) | ||||||
|  |         #-----------------------------------------------------------------------# | ||||||
|  |         #   计算步长 | ||||||
|  |         #   每一个特征点对应原来的图片上多少个像素点 | ||||||
|  |         #   [640, 640] 高的步长为640 / 20 = 32,宽的步长为640 / 20 = 32 | ||||||
|  |         #   如果特征层为20x20的话,一个特征点就对应原来的图片上的32个像素点 | ||||||
|  |         #   如果特征层为40x40的话,一个特征点就对应原来的图片上的16个像素点 | ||||||
|  |         #   如果特征层为80x80的话,一个特征点就对应原来的图片上的8个像素点 | ||||||
|  |         #   stride_h = stride_w = 32、16、8 | ||||||
|  |         #-----------------------------------------------------------------------# | ||||||
|  |         stride_h = self.input_shape[0] / in_h | ||||||
|  |         stride_w = self.input_shape[1] / in_w | ||||||
|  |         #-------------------------------------------------# | ||||||
|  |         #   此时获得的scaled_anchors大小是相对于特征层的 | ||||||
|  |         #-------------------------------------------------# | ||||||
|  |         scaled_anchors  = [(a_w / stride_w, a_h / stride_h) for a_w, a_h in self.anchors] | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   输入的input一共有三个,他们的shape分别是 | ||||||
|  |         #   bs, 3 * (5+num_classes), 20, 20 => bs, 3, 5 + num_classes, 20, 20 => batch_size, 3, 20, 20, 5 + num_classes | ||||||
|  |  | ||||||
|  |         #   batch_size, 3, 20, 20, 5 + num_classes | ||||||
|  |         #   batch_size, 3, 40, 40, 5 + num_classes | ||||||
|  |         #   batch_size, 3, 80, 80, 5 + num_classes | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         prediction = input.view(bs, len(self.anchors_mask[l]), self.bbox_attrs, in_h, in_w).permute(0, 1, 3, 4, 2).contiguous() | ||||||
|  |          | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   先验框的中心位置的调整参数 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         x = torch.sigmoid(prediction[..., 0]) | ||||||
|  |         y = torch.sigmoid(prediction[..., 1]) | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   先验框的宽高调整参数 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         w = torch.sigmoid(prediction[..., 2])  | ||||||
|  |         h = torch.sigmoid(prediction[..., 3])  | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   获得置信度,是否有物体 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         conf = torch.sigmoid(prediction[..., 4]) | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   种类置信度 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         pred_cls = torch.sigmoid(prediction[..., 5:]) | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         #   self.get_target已经合并到dataloader中 | ||||||
|  |         #   原因是在这里执行过慢,会大大延长训练时间 | ||||||
|  |         #-----------------------------------------------# | ||||||
|  |         # y_true, noobj_mask = self.get_target(l, targets, scaled_anchors, in_h, in_w) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------------------# | ||||||
|  |         #   将预测结果进行解码,判断预测结果和真实值的重合程度 | ||||||
|  |         #   如果重合程度过大则忽略,因为这些特征点属于预测比较准确的特征点 | ||||||
|  |         #   作为负样本不合适 | ||||||
|  |         #----------------------------------------------------------------# | ||||||
|  |         pred_boxes = self.get_pred_boxes(l, x, y, h, w, targets, scaled_anchors, in_h, in_w) | ||||||
|  |  | ||||||
|  |         if self.cuda: | ||||||
|  |             y_true          = y_true.type_as(x) | ||||||
|  |          | ||||||
|  |         loss    = 0 | ||||||
|  |         n       = torch.sum(y_true[..., 4] == 1) | ||||||
|  |         if n != 0: | ||||||
|  |             #---------------------------------------------------------------# | ||||||
|  |             #   计算预测结果和真实结果的giou,计算对应有真实框的先验框的giou损失 | ||||||
|  |             #                         loss_cls计算对应有真实框的先验框的分类损失 | ||||||
|  |             #----------------------------------------------------------------# | ||||||
|  |             giou        = self.box_giou(pred_boxes, y_true[..., :4]).type_as(x) | ||||||
|  |             loss_loc    = torch.mean((1 - giou)[y_true[..., 4] == 1]) | ||||||
|  |             loss_cls    = torch.mean(self.BCELoss(pred_cls[y_true[..., 4] == 1], self.smooth_labels(y_true[..., 5:][y_true[..., 4] == 1], self.label_smoothing, self.num_classes))) | ||||||
|  |             loss        += loss_loc * self.box_ratio + loss_cls * self.cls_ratio | ||||||
|  |             #-----------------------------------------------------------# | ||||||
|  |             #   计算置信度的loss | ||||||
|  |             #   也就意味着先验框对应的预测框预测的更准确 | ||||||
|  |             #   它才是用来预测这个物体的。 | ||||||
|  |             #-----------------------------------------------------------# | ||||||
|  |             tobj        = torch.where(y_true[..., 4] == 1, giou.detach().clamp(0), torch.zeros_like(y_true[..., 4])) | ||||||
|  |         else: | ||||||
|  |             tobj        = torch.zeros_like(y_true[..., 4]) | ||||||
|  |         loss_conf   = torch.mean(self.BCELoss(conf, tobj)) | ||||||
|  |          | ||||||
|  |         loss        += loss_conf * self.balance[l] * self.obj_ratio | ||||||
|  |         # if n != 0: | ||||||
|  |         #     print(loss_loc * self.box_ratio, loss_cls * self.cls_ratio, loss_conf * self.balance[l] * self.obj_ratio) | ||||||
|  |         return loss | ||||||
|  |      | ||||||
|  |     def get_near_points(self, x, y, i, j): | ||||||
|  |         sub_x = x - i | ||||||
|  |         sub_y = y - j | ||||||
|  |         if sub_x > 0.5 and sub_y > 0.5: | ||||||
|  |             return [[0, 0], [1, 0], [0, 1]] | ||||||
|  |         elif sub_x < 0.5 and sub_y > 0.5: | ||||||
|  |             return [[0, 0], [-1, 0], [0, 1]] | ||||||
|  |         elif sub_x < 0.5 and sub_y < 0.5: | ||||||
|  |             return [[0, 0], [-1, 0], [0, -1]] | ||||||
|  |         else: | ||||||
|  |             return [[0, 0], [1, 0], [0, -1]] | ||||||
|  |  | ||||||
|  |     def get_target(self, l, targets, anchors, in_h, in_w): | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         #   计算一共有多少张图片 | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         bs              = len(targets) | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         #   用于选取哪些先验框不包含物体 | ||||||
|  |         #   bs, 3, 20, 20 | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         noobj_mask      = torch.ones(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False) | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         #   帮助找到每一个先验框最对应的真实框 | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         box_best_ratio = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False) | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         #   batch_size, 3, 20, 20, 5 + num_classes | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         y_true          = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, self.bbox_attrs, requires_grad = False) | ||||||
|  |         for b in range(bs):             | ||||||
|  |             if len(targets[b])==0: | ||||||
|  |                 continue | ||||||
|  |             batch_target = torch.zeros_like(targets[b]) | ||||||
|  |             #-------------------------------------------------------# | ||||||
|  |             #   计算出正样本在特征层上的中心点 | ||||||
|  |             #   获得真实框相对于特征层的大小 | ||||||
|  |             #-------------------------------------------------------# | ||||||
|  |             batch_target[:, [0,2]] = targets[b][:, [0,2]] * in_w | ||||||
|  |             batch_target[:, [1,3]] = targets[b][:, [1,3]] * in_h | ||||||
|  |             batch_target[:, 4] = targets[b][:, 4] | ||||||
|  |             batch_target = batch_target.cpu() | ||||||
|  |              | ||||||
|  |             #-----------------------------------------------------------------------------# | ||||||
|  |             #   batch_target                                    : num_true_box, 5 | ||||||
|  |             #   batch_target[:, 2:4]                            : num_true_box, 2 | ||||||
|  |             #   torch.unsqueeze(batch_target[:, 2:4], 1)        : num_true_box, 1, 2 | ||||||
|  |             #   anchors                                         : 9, 2 | ||||||
|  |             #   torch.unsqueeze(torch.FloatTensor(anchors), 0)  : 1, 9, 2 | ||||||
|  |             #   ratios_of_gt_anchors    : num_true_box, 9, 2 | ||||||
|  |             #   ratios_of_anchors_gt    : num_true_box, 9, 2 | ||||||
|  |             # | ||||||
|  |             #   ratios                  : num_true_box, 9, 4 | ||||||
|  |             #   max_ratios              : num_true_box, 9    | ||||||
|  |             #   max_ratios每一个真实框和每一个先验框的最大宽高比! | ||||||
|  |             #------------------------------------------------------------------------------# | ||||||
|  |             ratios_of_gt_anchors = torch.unsqueeze(batch_target[:, 2:4], 1) / torch.unsqueeze(torch.FloatTensor(anchors), 0) | ||||||
|  |             ratios_of_anchors_gt = torch.unsqueeze(torch.FloatTensor(anchors), 0) /  torch.unsqueeze(batch_target[:, 2:4], 1) | ||||||
|  |             ratios               = torch.cat([ratios_of_gt_anchors, ratios_of_anchors_gt], dim = -1) | ||||||
|  |             max_ratios, _        = torch.max(ratios, dim = -1) | ||||||
|  |  | ||||||
|  |             for t, ratio in enumerate(max_ratios): | ||||||
|  |                 #-------------------------------------------------------# | ||||||
|  |                 #   ratio : 9 | ||||||
|  |                 #-------------------------------------------------------# | ||||||
|  |                 over_threshold = ratio < self.threshold | ||||||
|  |                 over_threshold[torch.argmin(ratio)] = True | ||||||
|  |                 for k, mask in enumerate(self.anchors_mask[l]): | ||||||
|  |                     if not over_threshold[mask]: | ||||||
|  |                         continue | ||||||
|  |                     #----------------------------------------# | ||||||
|  |                     #   获得真实框属于哪个网格点 | ||||||
|  |                     #   x  1.25     => 1 | ||||||
|  |                     #   y  3.75     => 3 | ||||||
|  |                     #----------------------------------------# | ||||||
|  |                     i = torch.floor(batch_target[t, 0]).long() | ||||||
|  |                     j = torch.floor(batch_target[t, 1]).long() | ||||||
|  |                      | ||||||
|  |                     offsets = self.get_near_points(batch_target[t, 0], batch_target[t, 1], i, j) | ||||||
|  |                     for offset in offsets: | ||||||
|  |                         local_i = i + offset[0] | ||||||
|  |                         local_j = j + offset[1] | ||||||
|  |  | ||||||
|  |                         if local_i >= in_w or local_i < 0 or local_j >= in_h or local_j < 0: | ||||||
|  |                             continue | ||||||
|  |  | ||||||
|  |                         if box_best_ratio[b, k, local_j, local_i] != 0: | ||||||
|  |                             if box_best_ratio[b, k, local_j, local_i] > ratio[mask]: | ||||||
|  |                                 y_true[b, k, local_j, local_i, :] = 0 | ||||||
|  |                             else: | ||||||
|  |                                 continue | ||||||
|  |                              | ||||||
|  |                         #----------------------------------------# | ||||||
|  |                         #   取出真实框的种类 | ||||||
|  |                         #----------------------------------------# | ||||||
|  |                         c = batch_target[t, 4].long() | ||||||
|  |  | ||||||
|  |                         #----------------------------------------# | ||||||
|  |                         #   noobj_mask代表无目标的特征点 | ||||||
|  |                         #----------------------------------------# | ||||||
|  |                         noobj_mask[b, k, local_j, local_i] = 0 | ||||||
|  |                         #----------------------------------------# | ||||||
|  |                         #   tx、ty代表中心调整参数的真实值 | ||||||
|  |                         #----------------------------------------# | ||||||
|  |                         y_true[b, k, local_j, local_i, 0] = batch_target[t, 0] | ||||||
|  |                         y_true[b, k, local_j, local_i, 1] = batch_target[t, 1] | ||||||
|  |                         y_true[b, k, local_j, local_i, 2] = batch_target[t, 2] | ||||||
|  |                         y_true[b, k, local_j, local_i, 3] = batch_target[t, 3] | ||||||
|  |                         y_true[b, k, local_j, local_i, 4] = 1 | ||||||
|  |                         y_true[b, k, local_j, local_i, c + 5] = 1 | ||||||
|  |                         #----------------------------------------# | ||||||
|  |                         #   获得当前先验框最好的比例 | ||||||
|  |                         #----------------------------------------# | ||||||
|  |                         box_best_ratio[b, k, local_j, local_i] = ratio[mask] | ||||||
|  |                          | ||||||
|  |         return y_true, noobj_mask | ||||||
|  |  | ||||||
|  |     def get_pred_boxes(self, l, x, y, h, w, targets, scaled_anchors, in_h, in_w): | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         #   计算一共有多少张图片 | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         bs = len(targets) | ||||||
|  |  | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         #   生成网格,先验框中心,网格左上角 | ||||||
|  |         #-----------------------------------------------------# | ||||||
|  |         grid_x = torch.linspace(0, in_w - 1, in_w).repeat(in_h, 1).repeat( | ||||||
|  |             int(bs * len(self.anchors_mask[l])), 1, 1).view(x.shape).type_as(x) | ||||||
|  |         grid_y = torch.linspace(0, in_h - 1, in_h).repeat(in_w, 1).t().repeat( | ||||||
|  |             int(bs * len(self.anchors_mask[l])), 1, 1).view(y.shape).type_as(x) | ||||||
|  |  | ||||||
|  |         # 生成先验框的宽高 | ||||||
|  |         scaled_anchors_l = np.array(scaled_anchors)[self.anchors_mask[l]] | ||||||
|  |         anchor_w = torch.Tensor(scaled_anchors_l).index_select(1, torch.LongTensor([0])).type_as(x) | ||||||
|  |         anchor_h = torch.Tensor(scaled_anchors_l).index_select(1, torch.LongTensor([1])).type_as(x) | ||||||
|  |          | ||||||
|  |         anchor_w = anchor_w.repeat(bs, 1).repeat(1, 1, in_h * in_w).view(w.shape) | ||||||
|  |         anchor_h = anchor_h.repeat(bs, 1).repeat(1, 1, in_h * in_w).view(h.shape) | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         #   计算调整后的先验框中心与宽高 | ||||||
|  |         #-------------------------------------------------------# | ||||||
|  |         pred_boxes_x    = torch.unsqueeze(x * 2. - 0.5 + grid_x, -1) | ||||||
|  |         pred_boxes_y    = torch.unsqueeze(y * 2. - 0.5 + grid_y, -1) | ||||||
|  |         pred_boxes_w    = torch.unsqueeze((w * 2) ** 2 * anchor_w, -1) | ||||||
|  |         pred_boxes_h    = torch.unsqueeze((h * 2) ** 2 * anchor_h, -1) | ||||||
|  |         pred_boxes      = torch.cat([pred_boxes_x, pred_boxes_y, pred_boxes_w, pred_boxes_h], dim = -1) | ||||||
|  |         return pred_boxes | ||||||
|  |  | ||||||
|  | def is_parallel(model): | ||||||
|  |     # Returns True if model is of type DP or DDP | ||||||
|  |     return type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel) | ||||||
|  |  | ||||||
|  | def de_parallel(model): | ||||||
|  |     # De-parallelize a model: returns single-GPU model if model is of type DP or DDP | ||||||
|  |     return model.module if is_parallel(model) else model | ||||||
|  |      | ||||||
|  | def copy_attr(a, b, include=(), exclude=()): | ||||||
|  |     # Copy attributes from b to a, options to only include [...] and to exclude [...] | ||||||
|  |     for k, v in b.__dict__.items(): | ||||||
|  |         if (len(include) and k not in include) or k.startswith('_') or k in exclude: | ||||||
|  |             continue | ||||||
|  |         else: | ||||||
|  |             setattr(a, k, v) | ||||||
|  |  | ||||||
|  | class ModelEMA: | ||||||
|  |     """ Updated Exponential Moving Average (EMA) from https://github.com/rwightman/pytorch-image-models | ||||||
|  |     Keeps a moving average of everything in the model state_dict (parameters and buffers) | ||||||
|  |     For EMA details see https://www.tensorflow.org/api_docs/python/tf/train/ExponentialMovingAverage | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def __init__(self, model, decay=0.9999, tau=2000, updates=0): | ||||||
|  |         # Create EMA | ||||||
|  |         self.ema = deepcopy(de_parallel(model)).eval()  # FP32 EMA | ||||||
|  |         # if next(model.parameters()).device.type != 'cpu': | ||||||
|  |         #     self.ema.half()  # FP16 EMA | ||||||
|  |         self.updates = updates  # number of EMA updates | ||||||
|  |         self.decay = lambda x: decay * (1 - math.exp(-x / tau))  # decay exponential ramp (to help early epochs) | ||||||
|  |         for p in self.ema.parameters(): | ||||||
|  |             p.requires_grad_(False) | ||||||
|  |  | ||||||
|  |     def update(self, model): | ||||||
|  |         # Update EMA parameters | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             self.updates += 1 | ||||||
|  |             d = self.decay(self.updates) | ||||||
|  |  | ||||||
|  |             msd = de_parallel(model).state_dict()  # model state_dict | ||||||
|  |             for k, v in self.ema.state_dict().items(): | ||||||
|  |                 if v.dtype.is_floating_point: | ||||||
|  |                     v *= d | ||||||
|  |                     v += (1 - d) * msd[k].detach() | ||||||
|  |  | ||||||
|  |     def update_attr(self, model, include=(), exclude=('process_group', 'reducer')): | ||||||
|  |         # Update EMA attributes | ||||||
|  |         copy_attr(self.ema, model, include, exclude) | ||||||
|  |  | ||||||
|  | def weights_init(net, init_type='normal', init_gain = 0.02): | ||||||
|  |     def init_func(m): | ||||||
|  |         classname = m.__class__.__name__ | ||||||
|  |         if hasattr(m, 'weight') and classname.find('Conv') != -1: | ||||||
|  |             if init_type == 'normal': | ||||||
|  |                 torch.nn.init.normal_(m.weight.data, 0.0, init_gain) | ||||||
|  |             elif init_type == 'xavier': | ||||||
|  |                 torch.nn.init.xavier_normal_(m.weight.data, gain=init_gain) | ||||||
|  |             elif init_type == 'kaiming': | ||||||
|  |                 torch.nn.init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') | ||||||
|  |             elif init_type == 'orthogonal': | ||||||
|  |                 torch.nn.init.orthogonal_(m.weight.data, gain=init_gain) | ||||||
|  |             else: | ||||||
|  |                 raise NotImplementedError('initialization method [%s] is not implemented' % init_type) | ||||||
|  |         elif classname.find('BatchNorm2d') != -1: | ||||||
|  |             torch.nn.init.normal_(m.weight.data, 1.0, 0.02) | ||||||
|  |             torch.nn.init.constant_(m.bias.data, 0.0) | ||||||
|  |     print('initialize network with %s type' % init_type) | ||||||
|  |     net.apply(init_func) | ||||||
|  |  | ||||||
|  | def get_lr_scheduler(lr_decay_type, lr, min_lr, total_iters, warmup_iters_ratio = 0.05, warmup_lr_ratio = 0.1, no_aug_iter_ratio = 0.05, step_num = 10): | ||||||
|  |     def yolox_warm_cos_lr(lr, min_lr, total_iters, warmup_total_iters, warmup_lr_start, no_aug_iter, iters): | ||||||
|  |         if iters <= warmup_total_iters: | ||||||
|  |             # lr = (lr - warmup_lr_start) * iters / float(warmup_total_iters) + warmup_lr_start | ||||||
|  |             lr = (lr - warmup_lr_start) * pow(iters / float(warmup_total_iters), 2 | ||||||
|  |             ) + warmup_lr_start | ||||||
|  |         elif iters >= total_iters - no_aug_iter: | ||||||
|  |             lr = min_lr | ||||||
|  |         else: | ||||||
|  |             lr = min_lr + 0.5 * (lr - min_lr) * ( | ||||||
|  |                 1.0 | ||||||
|  |                 + math.cos( | ||||||
|  |                     math.pi | ||||||
|  |                     * (iters - warmup_total_iters) | ||||||
|  |                     / (total_iters - warmup_total_iters - no_aug_iter) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         return lr | ||||||
|  |  | ||||||
|  |     def step_lr(lr, decay_rate, step_size, iters): | ||||||
|  |         if step_size < 1: | ||||||
|  |             raise ValueError("step_size must above 1.") | ||||||
|  |         n       = iters // step_size | ||||||
|  |         out_lr  = lr * decay_rate ** n | ||||||
|  |         return out_lr | ||||||
|  |  | ||||||
|  |     if lr_decay_type == "cos": | ||||||
|  |         warmup_total_iters  = min(max(warmup_iters_ratio * total_iters, 1), 3) | ||||||
|  |         warmup_lr_start     = max(warmup_lr_ratio * lr, 1e-6) | ||||||
|  |         no_aug_iter         = min(max(no_aug_iter_ratio * total_iters, 1), 15) | ||||||
|  |         func = partial(yolox_warm_cos_lr ,lr, min_lr, total_iters, warmup_total_iters, warmup_lr_start, no_aug_iter) | ||||||
|  |     else: | ||||||
|  |         decay_rate  = (min_lr / lr) ** (1 / (step_num - 1)) | ||||||
|  |         step_size   = total_iters / step_num | ||||||
|  |         func = partial(step_lr, lr, decay_rate, step_size) | ||||||
|  |  | ||||||
|  |     return func | ||||||
|  |  | ||||||
|  | def set_optimizer_lr(optimizer, lr_scheduler_func, epoch): | ||||||
|  |     lr = lr_scheduler_func(epoch) | ||||||
|  |     for param_group in optimizer.param_groups: | ||||||
|  |         param_group['lr'] = lr | ||||||
							
								
								
									
										192
									
								
								predict.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								predict.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | |||||||
|  | #-----------------------------------------------------------------------# | ||||||
|  | #   predict.py将单张图片预测、摄像头检测、FPS测试和目录遍历检测等功能 | ||||||
|  | #   整合到了一个py文件中,通过指定mode进行模式的修改。 | ||||||
|  | #-----------------------------------------------------------------------# | ||||||
|  | import time | ||||||
|  |  | ||||||
|  | import cv2 | ||||||
|  | import numpy as np | ||||||
|  | from PIL import Image | ||||||
|  |  | ||||||
|  | from yolo import YOLO, YOLO_ONNX | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     #----------------------------------------------------------------------------------------------------------# | ||||||
|  |     #   mode用于指定测试的模式: | ||||||
|  |     #   'predict'           表示单张图片预测,如果想对预测过程进行修改,如保存图片,截取对象等,可以先看下方详细的注释 | ||||||
|  |     #   'video'             表示视频检测,可调用摄像头或者视频进行检测,详情查看下方注释。 | ||||||
|  |     #   'fps'               表示测试fps,使用的图片是img里面的street.jpg,详情查看下方注释。 | ||||||
|  |     #   'dir_predict'       表示遍历文件夹进行检测并保存。默认遍历img文件夹,保存img_out文件夹,详情查看下方注释。 | ||||||
|  |     #   'heatmap'           表示进行预测结果的热力图可视化,详情查看下方注释。 | ||||||
|  |     #   'export_onnx'       表示将模型导出为onnx,需要pytorch1.7.1以上。 | ||||||
|  |     #   'predict_onnx'      表示利用导出的onnx模型进行预测,相关参数的修改在yolo.py_423行左右处的YOLO_ONNX | ||||||
|  |     #----------------------------------------------------------------------------------------------------------# | ||||||
|  |     mode = "predict" | ||||||
|  |     #-------------------------------------------------------------------------# | ||||||
|  |     #   crop                指定了是否在单张图片预测后对目标进行截取 | ||||||
|  |     #   count               指定了是否进行目标的计数 | ||||||
|  |     #   crop、count仅在mode='predict'时有效 | ||||||
|  |     #-------------------------------------------------------------------------# | ||||||
|  |     crop            = False | ||||||
|  |     count           = False | ||||||
|  |     #----------------------------------------------------------------------------------------------------------# | ||||||
|  |     #   video_path          用于指定视频的路径,当video_path=0时表示检测摄像头 | ||||||
|  |     #                       想要检测视频,则设置如video_path = "xxx.mp4"即可,代表读取出根目录下的xxx.mp4文件。 | ||||||
|  |     #   video_save_path     表示视频保存的路径,当video_save_path=""时表示不保存 | ||||||
|  |     #                       想要保存视频,则设置如video_save_path = "yyy.mp4"即可,代表保存为根目录下的yyy.mp4文件。 | ||||||
|  |     #   video_fps           用于保存的视频的fps | ||||||
|  |     # | ||||||
|  |     #   video_path、video_save_path和video_fps仅在mode='video'时有效 | ||||||
|  |     #   保存视频时需要ctrl+c退出或者运行到最后一帧才会完成完整的保存步骤。 | ||||||
|  |     #----------------------------------------------------------------------------------------------------------# | ||||||
|  |     video_path      = 0 | ||||||
|  |     video_save_path = "" | ||||||
|  |     video_fps       = 25.0 | ||||||
|  |     #----------------------------------------------------------------------------------------------------------# | ||||||
|  |     #   test_interval       用于指定测量fps的时候,图片检测的次数。理论上test_interval越大,fps越准确。 | ||||||
|  |     #   fps_image_path      用于指定测试的fps图片 | ||||||
|  |     #    | ||||||
|  |     #   test_interval和fps_image_path仅在mode='fps'有效 | ||||||
|  |     #----------------------------------------------------------------------------------------------------------# | ||||||
|  |     test_interval   = 100 | ||||||
|  |     fps_image_path  = "img/street.jpg" | ||||||
|  |     #-------------------------------------------------------------------------# | ||||||
|  |     #   dir_origin_path     指定了用于检测的图片的文件夹路径 | ||||||
|  |     #   dir_save_path       指定了检测完图片的保存路径 | ||||||
|  |     #    | ||||||
|  |     #   dir_origin_path和dir_save_path仅在mode='dir_predict'时有效 | ||||||
|  |     #-------------------------------------------------------------------------# | ||||||
|  |     dir_origin_path = "application\logs\logData\img" | ||||||
|  |     dir_save_path   = "application\logs\logData\save" | ||||||
|  |     #-------------------------------------------------------------------------# | ||||||
|  |     #   heatmap_save_path   热力图的保存路径,默认保存在model_data下 | ||||||
|  |     #    | ||||||
|  |     #   heatmap_save_path仅在mode='heatmap'有效 | ||||||
|  |     #-------------------------------------------------------------------------# | ||||||
|  |     heatmap_save_path = "model_data/heatmap_vision.png" | ||||||
|  |     #-------------------------------------------------------------------------# | ||||||
|  |     #   simplify            使用Simplify onnx | ||||||
|  |     #   onnx_save_path      指定了onnx的保存路径 | ||||||
|  |     #-------------------------------------------------------------------------# | ||||||
|  |     simplify        = True | ||||||
|  |     onnx_save_path  = "model_data/models.onnx" | ||||||
|  |  | ||||||
|  |     if mode != "predict_onnx": | ||||||
|  |         yolo = YOLO() | ||||||
|  |     else: | ||||||
|  |         yolo = YOLO_ONNX() | ||||||
|  |  | ||||||
|  |     if mode == "predict": | ||||||
|  |         ''' | ||||||
|  |         1、如果想要进行检测完的图片的保存,利用r_image.save("img.jpg")即可保存,直接在predict.py里进行修改即可。  | ||||||
|  |         2、如果想要获得预测框的坐标,可以进入yolo.detect_image函数,在绘图部分读取top,left,bottom,right这四个值。 | ||||||
|  |         3、如果想要利用预测框截取下目标,可以进入yolo.detect_image函数,在绘图部分利用获取到的top,left,bottom,right这四个值 | ||||||
|  |         在原图上利用矩阵的方式进行截取。 | ||||||
|  |         4、如果想要在预测图上写额外的字,比如检测到的特定目标的数量,可以进入yolo.detect_image函数,在绘图部分对predicted_class进行判断, | ||||||
|  |         比如判断if predicted_class == 'car': 即可判断当前目标是否为车,然后记录数量即可。利用draw.text即可写字。 | ||||||
|  |         ''' | ||||||
|  |         while True: | ||||||
|  |             img = input('Input image filename:') | ||||||
|  |             try: | ||||||
|  |                 image = Image.open(img) | ||||||
|  |             except: | ||||||
|  |                 print('Open Error! Try again!') | ||||||
|  |                 continue | ||||||
|  |             else: | ||||||
|  |                 r_image = yolo.detect_image(image, crop = crop, count=count) | ||||||
|  |                 r_image.show() | ||||||
|  |  | ||||||
|  |     elif mode == "video": | ||||||
|  |         capture = cv2.VideoCapture(video_path) | ||||||
|  |         if video_save_path!="": | ||||||
|  |             fourcc  = cv2.VideoWriter_fourcc(*'XVID') | ||||||
|  |             size    = (int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))) | ||||||
|  |             out     = cv2.VideoWriter(video_save_path, fourcc, video_fps, size) | ||||||
|  |  | ||||||
|  |         ref, frame = capture.read() | ||||||
|  |         if not ref: | ||||||
|  |             raise ValueError("未能正确读取摄像头(视频),请注意是否正确安装摄像头(是否正确填写视频路径)。") | ||||||
|  |  | ||||||
|  |         fps = 0.0 | ||||||
|  |         while(True): | ||||||
|  |             t1 = time.time() | ||||||
|  |             # 读取某一帧 | ||||||
|  |             ref, frame = capture.read() | ||||||
|  |             if not ref: | ||||||
|  |                 break | ||||||
|  |             # 格式转变,BGRtoRGB | ||||||
|  |             frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB) | ||||||
|  |             # 转变成Image | ||||||
|  |             frame = Image.fromarray(np.uint8(frame)) | ||||||
|  |             # 进行检测 | ||||||
|  |             frame = np.array(yolo.detect_image(frame)) | ||||||
|  |             # RGBtoBGR满足opencv显示格式 | ||||||
|  |             frame = cv2.cvtColor(frame,cv2.COLOR_RGB2BGR) | ||||||
|  |              | ||||||
|  |             fps  = ( fps + (1./(time.time()-t1)) ) / 2 | ||||||
|  |             print("fps= %.2f"%(fps)) | ||||||
|  |             frame = cv2.putText(frame, "fps= %.2f"%(fps), (0, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) | ||||||
|  |              | ||||||
|  |             cv2.imshow("video",frame) | ||||||
|  |             c= cv2.waitKey(1) & 0xff  | ||||||
|  |             if video_save_path!="": | ||||||
|  |                 out.write(frame) | ||||||
|  |  | ||||||
|  |             if c==27: | ||||||
|  |                 capture.release() | ||||||
|  |                 break | ||||||
|  |  | ||||||
|  |         print("Video Detection Done!") | ||||||
|  |         capture.release() | ||||||
|  |         if video_save_path!="": | ||||||
|  |             print("Save processed video to the path :" + video_save_path) | ||||||
|  |             out.release() | ||||||
|  |         cv2.destroyAllWindows() | ||||||
|  |          | ||||||
|  |     elif mode == "fps": | ||||||
|  |         img = Image.open(fps_image_path) | ||||||
|  |         tact_time = yolo.get_FPS(img, test_interval) | ||||||
|  |         print(str(tact_time) + ' seconds, ' + str(1/tact_time) + 'FPS, @batch_size 1') | ||||||
|  |  | ||||||
|  |     elif mode == "dir_predict": | ||||||
|  |         import os | ||||||
|  |  | ||||||
|  |         from tqdm import tqdm | ||||||
|  |  | ||||||
|  |         img_names = os.listdir(dir_origin_path) | ||||||
|  |         for img_name in tqdm(img_names): | ||||||
|  |             if img_name.lower().endswith(('.bmp', '.dib', '.png', '.jpg', '.jpeg', '.pbm', '.pgm', '.ppm', '.tif', '.tiff')): | ||||||
|  |                 image_path  = os.path.join(dir_origin_path, img_name) | ||||||
|  |                 image       = Image.open(image_path) | ||||||
|  |                 r_image     = yolo.detect_image(image) | ||||||
|  |                 if not os.path.exists(dir_save_path): | ||||||
|  |                     os.makedirs(dir_save_path) | ||||||
|  |                 r_image.save(os.path.join(dir_save_path, img_name.replace(".jpg", ".png")), quality=95, subsampling=0) | ||||||
|  |  | ||||||
|  |     elif mode == "heatmap": | ||||||
|  |         while True: | ||||||
|  |             img = input('Input image filename:') | ||||||
|  |             try: | ||||||
|  |                 image = Image.open(img) | ||||||
|  |             except: | ||||||
|  |                 print('Open Error! Try again!') | ||||||
|  |                 continue | ||||||
|  |             else: | ||||||
|  |                 yolo.detect_heatmap(image, heatmap_save_path) | ||||||
|  |                  | ||||||
|  |     elif mode == "export_onnx": | ||||||
|  |         yolo.convert_to_onnx(simplify, onnx_save_path) | ||||||
|  |  | ||||||
|  |     elif mode == "predict_onnx": | ||||||
|  |         while True: | ||||||
|  |             img = input('Input image filename:') | ||||||
|  |             try: | ||||||
|  |                 image = Image.open(img) | ||||||
|  |             except: | ||||||
|  |                 print('Open Error! Try again!') | ||||||
|  |                 continue | ||||||
|  |             else: | ||||||
|  |                 r_image = yolo.detect_image(image) | ||||||
|  |                 r_image.show() | ||||||
|  |     else: | ||||||
|  |         raise AssertionError("Please specify the correct mode: 'predict', 'video', 'fps', 'heatmap', 'export_onnx', 'dir_predict'.") | ||||||
							
								
								
									
										32
									
								
								summary.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								summary.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | #--------------------------------------------# | ||||||
|  | #   该部分代码用于看网络结构 | ||||||
|  | #--------------------------------------------# | ||||||
|  | import torch | ||||||
|  | from thop import clever_format, profile | ||||||
|  | from torchsummary import summary | ||||||
|  |  | ||||||
|  | from nets.yolo import YoloBody | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     input_shape     = [640, 640] | ||||||
|  |     anchors_mask    = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] | ||||||
|  |     num_classes     = 80 | ||||||
|  |     backbone        = 'cspdarknet' | ||||||
|  |     phi             = 'l' | ||||||
|  |      | ||||||
|  |     device  = torch.device("cuda" if torch.cuda.is_available() else "cpu") | ||||||
|  |     m       = YoloBody(anchors_mask, num_classes, phi, backbone=backbone).to(device) | ||||||
|  |     summary(m, (3, input_shape[0], input_shape[1])) | ||||||
|  |      | ||||||
|  |     dummy_input     = torch.randn(1, 3, input_shape[0], input_shape[1]).to(device) | ||||||
|  |     flops, params   = profile(m.to(device), (dummy_input, ), verbose=False) | ||||||
|  |     #--------------------------------------------------------# | ||||||
|  |     #   flops * 2是因为profile没有将卷积作为两个operations | ||||||
|  |     #   有些论文将卷积算乘法、加法两个operations。此时乘2 | ||||||
|  |     #   有些论文只考虑乘法的运算次数,忽略加法。此时不乘2 | ||||||
|  |     #   本代码选择乘2,参考YOLOX。 | ||||||
|  |     #--------------------------------------------------------# | ||||||
|  |     flops           = flops * 2 | ||||||
|  |     flops, params   = clever_format([flops, params], "%.3f") | ||||||
|  |     print('Total GFLOPS: %s' % (flops)) | ||||||
|  |     print('Total params: %s' % (params)) | ||||||
							
								
								
									
										11
									
								
								train.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								train.py
									
									
									
									
									
								
							| @@ -181,8 +181,8 @@ if __name__ == "__main__": | |||||||
|     #                           Adam可以使用相对较小的UnFreeze_Epoch |     #                           Adam可以使用相对较小的UnFreeze_Epoch | ||||||
|     #   Unfreeze_batch_size     模型在解冻后的batch_size |     #   Unfreeze_batch_size     模型在解冻后的batch_size | ||||||
|     #------------------------------------------------------------------# |     #------------------------------------------------------------------# | ||||||
|     UnFreeze_Epoch      = 500 |     UnFreeze_Epoch      = 100 | ||||||
|     Unfreeze_batch_size = 6 |     Unfreeze_batch_size = 4 | ||||||
|     #------------------------------------------------------------------# |     #------------------------------------------------------------------# | ||||||
|     #   Freeze_Train    是否进行冻结训练 |     #   Freeze_Train    是否进行冻结训练 | ||||||
|     #                   默认先冻结主干训练后解冻训练。 |     #                   默认先冻结主干训练后解冻训练。 | ||||||
| @@ -237,14 +237,14 @@ if __name__ == "__main__": | |||||||
|     #                   开启后会加快数据读取速度,但是会占用更多内存 |     #                   开启后会加快数据读取速度,但是会占用更多内存 | ||||||
|     #                   内存较小的电脑可以设置为2或者0   |     #                   内存较小的电脑可以设置为2或者0   | ||||||
|     #------------------------------------------------------------------# |     #------------------------------------------------------------------# | ||||||
|     num_workers         = 20 |     num_workers         = 6 | ||||||
|  |  | ||||||
|     #------------------------------------------------------# |     #------------------------------------------------------# | ||||||
|     #   train_annotation_path   训练图片路径和标签 |     #   train_annotation_path   训练图片路径和标签 | ||||||
|     #   val_annotation_path     验证图片路径和标签 |     #   val_annotation_path     验证图片路径和标签 | ||||||
|     #------------------------------------------------------# |     #------------------------------------------------------# | ||||||
|     train_annotation_path   = '2007_train.txt' |     train_annotation_path   = 'model_data/2007_train.txt' | ||||||
|     val_annotation_path     = '2007_val.txt' |     val_annotation_path     = 'model_data/2007_val.txt' | ||||||
|  |  | ||||||
|     seed_everything(seed) |     seed_everything(seed) | ||||||
|     #------------------------------------------------------# |     #------------------------------------------------------# | ||||||
| @@ -261,6 +261,7 @@ if __name__ == "__main__": | |||||||
|             print("Gpu Device Count : ", ngpus_per_node) |             print("Gpu Device Count : ", ngpus_per_node) | ||||||
|     else: |     else: | ||||||
|         device          = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |         device          = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | ||||||
|  |         print("\033[1;33;44mRuning on {}\033[0m".format(device)) | ||||||
|         local_rank      = 0 |         local_rank      = 0 | ||||||
|         rank            = 0 |         rank            = 0 | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										138
									
								
								utils/get_map.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								utils/get_map.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | |||||||
|  | import os | ||||||
|  | import xml.etree.ElementTree as ET | ||||||
|  |  | ||||||
|  | from PIL import Image | ||||||
|  | from tqdm import tqdm | ||||||
|  |  | ||||||
|  | from utils.utils import get_classes | ||||||
|  | from utils.utils_map import get_coco_map, get_map | ||||||
|  | from yolo import YOLO | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     ''' | ||||||
|  |     Recall和Precision不像AP是一个面积的概念,因此在门限值(Confidence)不同时,网络的Recall和Precision值是不同的。 | ||||||
|  |     默认情况下,本代码计算的Recall和Precision代表的是当门限值(Confidence)为0.5时,所对应的Recall和Precision值。 | ||||||
|  |  | ||||||
|  |     受到mAP计算原理的限制,网络在计算mAP时需要获得近乎所有的预测框,这样才可以计算不同门限条件下的Recall和Precision值 | ||||||
|  |     因此,本代码获得的map_out/detection-results/里面的txt的框的数量一般会比直接predict多一些,目的是列出所有可能的预测框, | ||||||
|  |     ''' | ||||||
|  |     #------------------------------------------------------------------------------------------------------------------# | ||||||
|  |     #   map_mode用于指定该文件运行时计算的内容 | ||||||
|  |     #   map_mode为0代表整个map计算流程,包括获得预测结果、获得真实框、计算VOC_map。 | ||||||
|  |     #   map_mode为1代表仅仅获得预测结果。 | ||||||
|  |     #   map_mode为2代表仅仅获得真实框。 | ||||||
|  |     #   map_mode为3代表仅仅计算VOC_map。 | ||||||
|  |     #   map_mode为4代表利用COCO工具箱计算当前数据集的0.50:0.95map。需要获得预测结果、获得真实框后并安装pycocotools才行 | ||||||
|  |     #-------------------------------------------------------------------------------------------------------------------# | ||||||
|  |     map_mode        = 0 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     #   此处的classes_path用于指定需要测量VOC_map的类别 | ||||||
|  |     #   一般情况下与训练和预测所用的classes_path一致即可 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     classes_path    = 'model_data/voc_classes.txt' | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     #   MINOVERLAP用于指定想要获得的mAP0.x,mAP0.x的意义是什么请同学们百度一下。 | ||||||
|  |     #   比如计算mAP0.75,可以设定MINOVERLAP = 0.75。 | ||||||
|  |     # | ||||||
|  |     #   当某一预测框与真实框重合度大于MINOVERLAP时,该预测框被认为是正样本,否则为负样本。 | ||||||
|  |     #   因此MINOVERLAP的值越大,预测框要预测的越准确才能被认为是正样本,此时算出来的mAP值越低, | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     MINOVERLAP      = 0.5 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     #   受到mAP计算原理的限制,网络在计算mAP时需要获得近乎所有的预测框,这样才可以计算mAP | ||||||
|  |     #   因此,confidence的值应当设置的尽量小进而获得全部可能的预测框。 | ||||||
|  |     #    | ||||||
|  |     #   该值一般不调整。因为计算mAP需要获得近乎所有的预测框,此处的confidence不能随便更改。 | ||||||
|  |     #   想要获得不同门限值下的Recall和Precision值,请修改下方的score_threhold。 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     confidence      = 0.001 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     #   预测时使用到的非极大抑制值的大小,越大表示非极大抑制越不严格。 | ||||||
|  |     #    | ||||||
|  |     #   该值一般不调整。 | ||||||
|  |     #--------------------------------------------------------------------------------------# | ||||||
|  |     nms_iou         = 0.5 | ||||||
|  |     #---------------------------------------------------------------------------------------------------------------# | ||||||
|  |     #   Recall和Precision不像AP是一个面积的概念,因此在门限值不同时,网络的Recall和Precision值是不同的。 | ||||||
|  |     #    | ||||||
|  |     #   默认情况下,本代码计算的Recall和Precision代表的是当门限值为0.5(此处定义为score_threhold)时所对应的Recall和Precision值。 | ||||||
|  |     #   因为计算mAP需要获得近乎所有的预测框,上面定义的confidence不能随便更改。 | ||||||
|  |     #   这里专门定义一个score_threhold用于代表门限值,进而在计算mAP时找到门限值对应的Recall和Precision值。 | ||||||
|  |     #---------------------------------------------------------------------------------------------------------------# | ||||||
|  |     score_threhold  = 0.5 | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     #   map_vis用于指定是否开启VOC_map计算的可视化 | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     map_vis         = False | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     #   指向VOC数据集所在的文件夹 | ||||||
|  |     #   默认指向根目录下的VOC数据集 | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     VOCdevkit_path  = 'VOCdevkit' | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     #   结果输出的文件夹,默认为map_out | ||||||
|  |     #-------------------------------------------------------# | ||||||
|  |     map_out_path    = 'map_out' | ||||||
|  |  | ||||||
|  |     image_ids = open(os.path.join(VOCdevkit_path, "VOC2007/ImageSets/Main/test.txt")).read().strip().split() | ||||||
|  |  | ||||||
|  |     if not os.path.exists(map_out_path): | ||||||
|  |         os.makedirs(map_out_path) | ||||||
|  |     if not os.path.exists(os.path.join(map_out_path, 'ground-truth')): | ||||||
|  |         os.makedirs(os.path.join(map_out_path, 'ground-truth')) | ||||||
|  |     if not os.path.exists(os.path.join(map_out_path, 'detection-results')): | ||||||
|  |         os.makedirs(os.path.join(map_out_path, 'detection-results')) | ||||||
|  |     if not os.path.exists(os.path.join(map_out_path, 'images-optional')): | ||||||
|  |         os.makedirs(os.path.join(map_out_path, 'images-optional')) | ||||||
|  |  | ||||||
|  |     class_names, _ = get_classes(classes_path) | ||||||
|  |  | ||||||
|  |     if map_mode == 0 or map_mode == 1: | ||||||
|  |         print("Load model.") | ||||||
|  |         yolo = YOLO(confidence = confidence, nms_iou = nms_iou) | ||||||
|  |         print("Load model done.") | ||||||
|  |  | ||||||
|  |         print("Get predict result.") | ||||||
|  |         for image_id in tqdm(image_ids): | ||||||
|  |             image_path  = os.path.join(VOCdevkit_path, "VOC2007/JPEGImages/"+image_id+".jpg") | ||||||
|  |             image       = Image.open(image_path) | ||||||
|  |             if map_vis: | ||||||
|  |                 image.save(os.path.join(map_out_path, "images-optional/" + image_id + ".jpg")) | ||||||
|  |             yolo.get_map_txt(image_id, image, class_names, map_out_path) | ||||||
|  |         print("Get predict result done.") | ||||||
|  |          | ||||||
|  |     if map_mode == 0 or map_mode == 2: | ||||||
|  |         print("Get ground truth result.") | ||||||
|  |         for image_id in tqdm(image_ids): | ||||||
|  |             with open(os.path.join(map_out_path, "ground-truth/"+image_id+".txt"), "w") as new_f: | ||||||
|  |                 root = ET.parse(os.path.join(VOCdevkit_path, "VOC2007/Annotations/"+image_id+".xml")).getroot() | ||||||
|  |                 for obj in root.findall('object'): | ||||||
|  |                     difficult_flag = False | ||||||
|  |                     if obj.find('difficult')!=None: | ||||||
|  |                         difficult = obj.find('difficult').text | ||||||
|  |                         if int(difficult)==1: | ||||||
|  |                             difficult_flag = True | ||||||
|  |                     obj_name = obj.find('name').text | ||||||
|  |                     if obj_name not in class_names: | ||||||
|  |                         continue | ||||||
|  |                     bndbox  = obj.find('bndbox') | ||||||
|  |                     left    = bndbox.find('xmin').text | ||||||
|  |                     top     = bndbox.find('ymin').text | ||||||
|  |                     right   = bndbox.find('xmax').text | ||||||
|  |                     bottom  = bndbox.find('ymax').text | ||||||
|  |  | ||||||
|  |                     if difficult_flag: | ||||||
|  |                         new_f.write("%s %s %s %s %s difficult\n" % (obj_name, left, top, right, bottom)) | ||||||
|  |                     else: | ||||||
|  |                         new_f.write("%s %s %s %s %s\n" % (obj_name, left, top, right, bottom)) | ||||||
|  |         print("Get ground truth result done.") | ||||||
|  |  | ||||||
|  |     if map_mode == 0 or map_mode == 3: | ||||||
|  |         print("Get map.") | ||||||
|  |         get_map(MINOVERLAP, True, score_threhold = score_threhold, path = map_out_path) | ||||||
|  |         print("Get map done.") | ||||||
|  |  | ||||||
|  |     if map_mode == 4: | ||||||
|  |         print("Get map.") | ||||||
|  |         get_coco_map(class_names = class_names, path = map_out_path) | ||||||
|  |         print("Get map done.") | ||||||
							
								
								
									
										32
									
								
								utils/summary.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								utils/summary.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | #--------------------------------------------# | ||||||
|  | #   该部分代码用于看网络结构 | ||||||
|  | #--------------------------------------------# | ||||||
|  | import torch | ||||||
|  | from thop import clever_format, profile | ||||||
|  | from torchsummary import summary | ||||||
|  |  | ||||||
|  | from nets.yolo import YoloBody | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     input_shape     = [640, 640] | ||||||
|  |     anchors_mask    = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] | ||||||
|  |     num_classes     = 80 | ||||||
|  |     backbone        = 'cspdarknet' | ||||||
|  |     phi             = 'l' | ||||||
|  |      | ||||||
|  |     device  = torch.device("cuda" if torch.cuda.is_available() else "cpu") | ||||||
|  |     m       = YoloBody(anchors_mask, num_classes, phi, backbone=backbone).to(device) | ||||||
|  |     summary(m, (3, input_shape[0], input_shape[1])) | ||||||
|  |      | ||||||
|  |     dummy_input     = torch.randn(1, 3, input_shape[0], input_shape[1]).to(device) | ||||||
|  |     flops, params   = profile(m.to(device), (dummy_input, ), verbose=False) | ||||||
|  |     #--------------------------------------------------------# | ||||||
|  |     #   flops * 2是因为profile没有将卷积作为两个operations | ||||||
|  |     #   有些论文将卷积算乘法、加法两个operations。此时乘2 | ||||||
|  |     #   有些论文只考虑乘法的运算次数,忽略加法。此时不乘2 | ||||||
|  |     #   本代码选择乘2,参考YOLOX。 | ||||||
|  |     #--------------------------------------------------------# | ||||||
|  |     flops           = flops * 2 | ||||||
|  |     flops, params   = clever_format([flops, params], "%.3f") | ||||||
|  |     print('Total GFLOPS: %s' % (flops)) | ||||||
|  |     print('Total params: %s' % (params)) | ||||||
| @@ -3,9 +3,13 @@ import random | |||||||
| import xml.etree.ElementTree as ET | import xml.etree.ElementTree as ET | ||||||
| 
 | 
 | ||||||
| import numpy as np | import numpy as np | ||||||
|  | import sys,os | ||||||
|  | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | ||||||
| 
 | 
 | ||||||
| from utils.utils import get_classes | from utils import get_classes | ||||||
| 
 | import configparser | ||||||
|  | conf=configparser.ConfigParser() | ||||||
|  | conf.read('config.ini',encoding='utf-8') | ||||||
| #--------------------------------------------------------------------------------------------------------------------------------# | #--------------------------------------------------------------------------------------------------------------------------------# | ||||||
| #   annotation_mode用于指定该文件运行时计算的内容 | #   annotation_mode用于指定该文件运行时计算的内容 | ||||||
| #   annotation_mode为0代表整个标签处理过程,包括获得VOCdevkit/VOC2007/ImageSets里面的txt以及训练用的2007_train.txt、2007_val.txt | #   annotation_mode为0代表整个标签处理过程,包括获得VOCdevkit/VOC2007/ImageSets里面的txt以及训练用的2007_train.txt、2007_val.txt | ||||||
| @@ -20,7 +24,7 @@ annotation_mode     = 0 | |||||||
| #   那么就是因为classes没有设定正确 | #   那么就是因为classes没有设定正确 | ||||||
| #   仅在annotation_mode为0和2的时候有效 | #   仅在annotation_mode为0和2的时候有效 | ||||||
| #-------------------------------------------------------------------# | #-------------------------------------------------------------------# | ||||||
| classes_path        = 'trainYolov5-v6/model_data/voc_classes.txt' | classes_path        = conf.get('dataset', 'classes_path') | ||||||
| #--------------------------------------------------------------------------------------------------------------------------------# | #--------------------------------------------------------------------------------------------------------------------------------# | ||||||
| #   trainval_percent用于指定(训练集+验证集)与测试集的比例,默认情况下 (训练集+验证集):测试集 = 9:1 | #   trainval_percent用于指定(训练集+验证集)与测试集的比例,默认情况下 (训练集+验证集):测试集 = 9:1 | ||||||
| #   train_percent用于指定(训练集+验证集)中训练集与验证集的比例,默认情况下 训练集:验证集 = 9:1 | #   train_percent用于指定(训练集+验证集)中训练集与验证集的比例,默认情况下 训练集:验证集 = 9:1 | ||||||
| @@ -32,7 +36,7 @@ train_percent       = 0.9 | |||||||
| #   指向VOC数据集所在的文件夹 | #   指向VOC数据集所在的文件夹 | ||||||
| #   默认指向根目录下的VOC数据集 | #   默认指向根目录下的VOC数据集 | ||||||
| #-------------------------------------------------------# | #-------------------------------------------------------# | ||||||
| VOCdevkit_path  = 'Data/TrainData' | VOCdevkit_path  = r'database/Train' | ||||||
| 
 | 
 | ||||||
| VOCdevkit_sets  = [('2007', 'train'), ('2007', 'val')] | VOCdevkit_sets  = [('2007', 'train'), ('2007', 'val')] | ||||||
| classes, _      = get_classes(classes_path) | classes, _      = get_classes(classes_path) | ||||||
| @@ -112,9 +116,9 @@ if __name__ == "__main__": | |||||||
|         type_index = 0 |         type_index = 0 | ||||||
|         for year, image_set in VOCdevkit_sets: |         for year, image_set in VOCdevkit_sets: | ||||||
|             image_ids = open(os.path.join(VOCdevkit_path, 'ImageSets/Main/%s.txt'%(image_set)), encoding='utf-8').read().strip().split() |             image_ids = open(os.path.join(VOCdevkit_path, 'ImageSets/Main/%s.txt'%(image_set)), encoding='utf-8').read().strip().split() | ||||||
|             list_file = open('%s_%s.txt'%(year, image_set), 'w', encoding='utf-8') |             list_file = open('model_data/%s_%s.txt'%(year, image_set), 'w', encoding='utf-8') | ||||||
|             for image_id in image_ids: |             for image_id in image_ids: | ||||||
|                 list_file.write('%s/JPEGImages/%s.jpg'%(os.path.abspath(VOCdevkit_path), image_id)) |                 list_file.write('%s/JPEGImages/%s.png'%(os.path.abspath(VOCdevkit_path), image_id)) | ||||||
| 
 | 
 | ||||||
|                 convert_annotation(year, image_id, list_file) |                 convert_annotation(year, image_id, list_file) | ||||||
|                 list_file.write('\n') |                 list_file.write('\n') | ||||||
							
								
								
									
										663
									
								
								utils/yolo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										663
									
								
								utils/yolo.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,663 @@ | |||||||
|  | import colorsys | ||||||
|  | import os | ||||||
|  | import time | ||||||
|  |  | ||||||
|  | import cv2 | ||||||
|  | import numpy as np | ||||||
|  | import torch | ||||||
|  | import torch.nn as nn | ||||||
|  | from PIL import ImageDraw, ImageFont, Image | ||||||
|  |  | ||||||
|  | from nets.yolo import YoloBody | ||||||
|  | from utils.utils import (cvtColor, get_anchors, get_classes, preprocess_input, | ||||||
|  |                          resize_image, show_config) | ||||||
|  | from utils.utils_bbox import DecodeBox, DecodeBoxNP | ||||||
|  |  | ||||||
|  | ''' | ||||||
|  | 训练自己的数据集必看注释! | ||||||
|  | ''' | ||||||
|  | class YOLO(object): | ||||||
|  |     _defaults = { | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   使用自己训练好的模型进行预测一定要修改model_path和classes_path! | ||||||
|  |         #   model_path指向logs文件夹下的权值文件,classes_path指向model_data下的txt | ||||||
|  |         # | ||||||
|  |         #   训练好后logs文件夹下存在多个权值文件,选择验证集损失较低的即可。 | ||||||
|  |         #   验证集损失较低不代表mAP较高,仅代表该权值在验证集上泛化性能较好。 | ||||||
|  |         #   如果出现shape不匹配,同时要注意训练时的model_path和classes_path参数的修改 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         "model_path"        : r'logs-yolov5\1.pth', | ||||||
|  |         "classes_path"      : 'trainYolov5-v6\\model_data/coco_classes.txt', | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   anchors_path代表先验框对应的txt文件,一般不修改。 | ||||||
|  |         #   anchors_mask用于帮助代码找到对应的先验框,一般不修改。 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "anchors_path"      : 'trainYolov5-v6\\model_data/yolo_anchors.txt', | ||||||
|  |         "anchors_mask"      : [[6, 7, 8], [3, 4, 5], [0, 1, 2]], | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   输入图片的大小,必须为32的倍数。 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "input_shape"       : [640, 640], | ||||||
|  |         #------------------------------------------------------# | ||||||
|  |         #   backbone        cspdarknet(默认) | ||||||
|  |         #                   convnext_tiny | ||||||
|  |         #                   convnext_small | ||||||
|  |         #                   swin_transfomer_tiny | ||||||
|  |         #------------------------------------------------------# | ||||||
|  |         "backbone"          : 'cspdarknet', | ||||||
|  |         #------------------------------------------------------# | ||||||
|  |         #   所使用的YoloV5的版本。s、m、l、x | ||||||
|  |         #   在除cspdarknet的其它主干中仅影响panet的大小 | ||||||
|  |         #------------------------------------------------------# | ||||||
|  |         "phi"               : 's', | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   只有得分大于置信度的预测框会被保留下来 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "confidence"        : 0.5, | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   非极大抑制所用到的nms_iou大小 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "nms_iou"           : 0.3, | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   该变量用于控制是否使用letterbox_image对输入图像进行不失真的resize, | ||||||
|  |         #   在多次测试后,发现关闭letterbox_image直接resize的效果更好 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "letterbox_image"   : True, | ||||||
|  |         #-------------------------------# | ||||||
|  |         #   是否使用Cuda | ||||||
|  |         #   没有GPU可以设置成False | ||||||
|  |         #-------------------------------# | ||||||
|  |         "cuda"              : True, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def get_defaults(cls, n): | ||||||
|  |         if n in cls._defaults: | ||||||
|  |             return cls._defaults[n] | ||||||
|  |         else: | ||||||
|  |             return "Unrecognized attribute name '" + n + "'" | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   初始化YOLO | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def __init__(self, **kwargs): | ||||||
|  |         self.__dict__.update(self._defaults) | ||||||
|  |         for name, value in kwargs.items(): | ||||||
|  |             setattr(self, name, value) | ||||||
|  |             self._defaults[name] = value  | ||||||
|  |              | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   获得种类和先验框的数量 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         self.class_names, self.num_classes  = get_classes(self.classes_path) | ||||||
|  |         self.anchors, self.num_anchors      = get_anchors(self.anchors_path) | ||||||
|  |         self.bbox_util                      = DecodeBox(self.anchors, self.num_classes, (self.input_shape[0], self.input_shape[1]), self.anchors_mask) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   画框设置不同的颜色 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)] | ||||||
|  |         self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) | ||||||
|  |         self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors)) | ||||||
|  |         self.generate() | ||||||
|  |  | ||||||
|  |         show_config(**self._defaults) | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   生成模型 | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def generate(self, onnx=False): | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   建立yolo模型,载入yolo模型的权重 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         self.net    = YoloBody(self.anchors_mask, self.num_classes, self.phi, backbone = self.backbone, input_shape = self.input_shape) | ||||||
|  |         device      = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | ||||||
|  |         self.net.load_state_dict(torch.load(self.model_path, map_location=device),strict=False) | ||||||
|  |         self.net    = self.net.eval() | ||||||
|  |         print('{} model, and classes loaded.'.format(self.model_path)) | ||||||
|  |         if not onnx: | ||||||
|  |             if self.cuda: | ||||||
|  |                 self.net = nn.DataParallel(self.net) | ||||||
|  |                 self.net = self.net.cuda() | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   检测图片 | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def detect_image(self, image, crop = False, count = False): | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   计算输入图片的高和宽 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         image_shape = np.array(np.shape(image)[0:2]) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   给图像增加灰条,实现不失真的resize | ||||||
|  |         #   也可以直接resize进行识别 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             images = torch.from_numpy(image_data) | ||||||
|  |             if self.cuda: | ||||||
|  |                 images = images.cuda() | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将图像输入网络当中进行预测! | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             outputs = self.net(images) | ||||||
|  |             outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                         image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou) | ||||||
|  |                                                      | ||||||
|  |             if results[0] is None:  | ||||||
|  |                 return image | ||||||
|  |  | ||||||
|  |             top_label   = np.array(results[0][:, 6], dtype = 'int32') | ||||||
|  |             top_conf    = results[0][:, 4] * results[0][:, 5] | ||||||
|  |             top_boxes   = results[0][:, :4] | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   设置字体与边框厚度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         font        = ImageFont.truetype(font='model_data/simhei.ttf', size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32')) | ||||||
|  |         thickness   = int(max((image.size[0] + image.size[1]) // np.mean(self.input_shape), 1)) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   计数 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         if count: | ||||||
|  |             print("top_label:", top_label) | ||||||
|  |             classes_nums    = np.zeros([self.num_classes]) | ||||||
|  |             for i in range(self.num_classes): | ||||||
|  |                 num = np.sum(top_label == i) | ||||||
|  |                 if num > 0: | ||||||
|  |                     print(self.class_names[i], " : ", num) | ||||||
|  |                 classes_nums[i] = num | ||||||
|  |             print("classes_nums:", classes_nums) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   是否进行目标的裁剪 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         if crop: | ||||||
|  |             for i, c in list(enumerate(top_boxes)): | ||||||
|  |                 top, left, bottom, right = top_boxes[i] | ||||||
|  |                 top     = max(0, np.floor(top).astype('int32')) | ||||||
|  |                 left    = max(0, np.floor(left).astype('int32')) | ||||||
|  |                 bottom  = min(image.size[1], np.floor(bottom).astype('int32')) | ||||||
|  |                 right   = min(image.size[0], np.floor(right).astype('int32')) | ||||||
|  |                  | ||||||
|  |                 dir_save_path = "img_crop" | ||||||
|  |                 if not os.path.exists(dir_save_path): | ||||||
|  |                     os.makedirs(dir_save_path) | ||||||
|  |                 crop_image = image.crop([left, top, right, bottom]) | ||||||
|  |                 crop_image.save(os.path.join(dir_save_path, "crop_" + str(i) + ".png"), quality=95, subsampling=0) | ||||||
|  |                 print("save crop_" + str(i) + ".png to " + dir_save_path) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   图像绘制 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         for i, c in list(enumerate(top_label)): | ||||||
|  |             predicted_class = self.class_names[int(c)] | ||||||
|  |             box             = top_boxes[i] | ||||||
|  |             score           = top_conf[i] | ||||||
|  |  | ||||||
|  |             top, left, bottom, right = box | ||||||
|  |  | ||||||
|  |             top     = max(0, np.floor(top).astype('int32')) | ||||||
|  |             left    = max(0, np.floor(left).astype('int32')) | ||||||
|  |             bottom  = min(image.size[1], np.floor(bottom).astype('int32')) | ||||||
|  |             right   = min(image.size[0], np.floor(right).astype('int32')) | ||||||
|  |  | ||||||
|  |             label = '{} {:.2f}'.format(predicted_class, score) | ||||||
|  |             draw = ImageDraw.Draw(image) | ||||||
|  |             label_size = draw.textsize(label, font) | ||||||
|  |             label = label.encode('utf-8') | ||||||
|  |             print(label, top, left, bottom, right) | ||||||
|  |              | ||||||
|  |             if top - label_size[1] >= 0: | ||||||
|  |                 text_origin = np.array([left, top - label_size[1]]) | ||||||
|  |             else: | ||||||
|  |                 text_origin = np.array([left, top + 1]) | ||||||
|  |  | ||||||
|  |             for i in range(thickness): | ||||||
|  |                 draw.rectangle([left + i, top + i, right - i, bottom - i], outline=self.colors[c]) | ||||||
|  |             draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)], fill=self.colors[c]) | ||||||
|  |             draw.text(text_origin, str(label,'UTF-8'), fill=(0, 0, 0), font=font) | ||||||
|  |             del draw | ||||||
|  |  | ||||||
|  |         return image | ||||||
|  |  | ||||||
|  |     def get_FPS(self, image, test_interval): | ||||||
|  |         image_shape = np.array(np.shape(image)[0:2]) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   给图像增加灰条,实现不失真的resize | ||||||
|  |         #   也可以直接resize进行识别 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             images = torch.from_numpy(image_data) | ||||||
|  |             if self.cuda: | ||||||
|  |                 images = images.cuda() | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将图像输入网络当中进行预测! | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             outputs = self.net(images) | ||||||
|  |             outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                         image_shape, self.letterbox_image, conf_thres=self.confidence, nms_thres=self.nms_iou) | ||||||
|  |                                                      | ||||||
|  |         t1 = time.time() | ||||||
|  |         for _ in range(test_interval): | ||||||
|  |             with torch.no_grad(): | ||||||
|  |                 #---------------------------------------------------------# | ||||||
|  |                 #   将图像输入网络当中进行预测! | ||||||
|  |                 #---------------------------------------------------------# | ||||||
|  |                 outputs = self.net(images) | ||||||
|  |                 outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |                 #---------------------------------------------------------# | ||||||
|  |                 #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |                 #---------------------------------------------------------# | ||||||
|  |                 results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                             image_shape, self.letterbox_image, conf_thres=self.confidence, nms_thres=self.nms_iou) | ||||||
|  |                              | ||||||
|  |         t2 = time.time() | ||||||
|  |         tact_time = (t2 - t1) / test_interval | ||||||
|  |         return tact_time | ||||||
|  |  | ||||||
|  |     def detect_heatmap(self, image, heatmap_save_path): | ||||||
|  |         import cv2 | ||||||
|  |         import matplotlib.pyplot as plt | ||||||
|  |         def sigmoid(x): | ||||||
|  |             y = 1.0 / (1.0 + np.exp(-x)) | ||||||
|  |             return y | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   给图像增加灰条,实现不失真的resize | ||||||
|  |         #   也可以直接resize进行识别 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = resize_image(image, (self.input_shape[1],self.input_shape[0]), self.letterbox_image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             images = torch.from_numpy(image_data) | ||||||
|  |             if self.cuda: | ||||||
|  |                 images = images.cuda() | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将图像输入网络当中进行预测! | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             outputs = self.net(images) | ||||||
|  |          | ||||||
|  |         plt.imshow(image, alpha=1) | ||||||
|  |         plt.axis('off') | ||||||
|  |         mask    = np.zeros((image.size[1], image.size[0])) | ||||||
|  |         for sub_output in outputs: | ||||||
|  |             sub_output = sub_output.cpu().numpy() | ||||||
|  |             b, c, h, w = np.shape(sub_output) | ||||||
|  |             sub_output = np.transpose(np.reshape(sub_output, [b, 3, -1, h, w]), [0, 3, 4, 1, 2])[0] | ||||||
|  |             score      = np.max(sigmoid(sub_output[..., 4]), -1) | ||||||
|  |             score      = cv2.resize(score, (image.size[0], image.size[1])) | ||||||
|  |             normed_score    = (score * 255).astype('uint8') | ||||||
|  |             mask            = np.maximum(mask, normed_score) | ||||||
|  |              | ||||||
|  |         plt.imshow(mask, alpha=0.5, interpolation='nearest', cmap="jet") | ||||||
|  |  | ||||||
|  |         plt.axis('off') | ||||||
|  |         plt.subplots_adjust(top=1, bottom=0, right=1,  left=0, hspace=0, wspace=0) | ||||||
|  |         plt.margins(0, 0) | ||||||
|  |         plt.savefig(heatmap_save_path, dpi=200, bbox_inches='tight', pad_inches = -0.1) | ||||||
|  |         print("Save to the " + heatmap_save_path) | ||||||
|  |         plt.show() | ||||||
|  |  | ||||||
|  |     def convert_to_onnx(self, simplify, model_path): | ||||||
|  |         import onnx | ||||||
|  |         self.generate(onnx=True) | ||||||
|  |  | ||||||
|  |         im                  = torch.zeros(1, 3, *self.input_shape).to('cpu')  # image size(1, 3, 512, 512) BCHW | ||||||
|  |         input_layer_names   = ["images"] | ||||||
|  |         output_layer_names  = ["output"] | ||||||
|  |          | ||||||
|  |         # Export the model | ||||||
|  |         print(f'Starting export with onnx {onnx.__version__}.') | ||||||
|  |         torch.onnx.export(self.net, | ||||||
|  |                         im, | ||||||
|  |                         f               = model_path, | ||||||
|  |                         verbose         = False, | ||||||
|  |                         opset_version   = 12, | ||||||
|  |                         training        = torch.onnx.TrainingMode.EVAL, | ||||||
|  |                         do_constant_folding = True, | ||||||
|  |                         input_names     = input_layer_names, | ||||||
|  |                         output_names    = output_layer_names, | ||||||
|  |                         dynamic_axes    = None) | ||||||
|  |  | ||||||
|  |         # Checks | ||||||
|  |         model_onnx = onnx.load(model_path)  # load onnx model | ||||||
|  |         onnx.checker.check_model(model_onnx)  # check onnx model | ||||||
|  |  | ||||||
|  |         # Simplify onnx | ||||||
|  |         if simplify: | ||||||
|  |             import onnxsim | ||||||
|  |             print(f'Simplifying with onnx-simplifier {onnxsim.__version__}.') | ||||||
|  |             model_onnx, check = onnxsim.simplify( | ||||||
|  |                 model_onnx, | ||||||
|  |                 dynamic_input_shape=False, | ||||||
|  |                 input_shapes=None) | ||||||
|  |             assert check, 'assert check failed' | ||||||
|  |             onnx.save(model_onnx, model_path) | ||||||
|  |  | ||||||
|  |         print('Onnx model save as {}'.format(model_path)) | ||||||
|  |  | ||||||
|  |     def get_map_txt(self, image_id, image, class_names, map_out_path): | ||||||
|  |         f = open(os.path.join(map_out_path, "detection-results/"+image_id+".txt"), "w", encoding='utf-8')  | ||||||
|  |         image_shape = np.array(np.shape(image)[0:2]) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   给图像增加灰条,实现不失真的resize | ||||||
|  |         #   也可以直接resize进行识别 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             images = torch.from_numpy(image_data) | ||||||
|  |             if self.cuda: | ||||||
|  |                 images = images.cuda() | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将图像输入网络当中进行预测! | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             outputs = self.net(images) | ||||||
|  |             outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                         image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou) | ||||||
|  |                                                      | ||||||
|  |             if results[0] is None:  | ||||||
|  |                 return  | ||||||
|  |  | ||||||
|  |             top_label   = np.array(results[0][:, 6], dtype = 'int32') | ||||||
|  |             top_conf    = results[0][:, 4] * results[0][:, 5] | ||||||
|  |             top_boxes   = results[0][:, :4] | ||||||
|  |  | ||||||
|  |         for i, c in list(enumerate(top_label)): | ||||||
|  |             predicted_class = self.class_names[int(c)] | ||||||
|  |             box             = top_boxes[i] | ||||||
|  |             score           = str(top_conf[i]) | ||||||
|  |  | ||||||
|  |             top, left, bottom, right = box | ||||||
|  |             if predicted_class not in class_names: | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             f.write("%s %s %s %s %s %s\n" % (predicted_class, score[:6], str(int(left)), str(int(top)), str(int(right)),str(int(bottom)))) | ||||||
|  |  | ||||||
|  |         f.close() | ||||||
|  |         return  | ||||||
|  |  | ||||||
|  | class YOLO_ONNX(object): | ||||||
|  |     _defaults = { | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   使用自己训练好的模型进行预测一定要修改onnx_path和classes_path! | ||||||
|  |         #   onnx_path指向logs文件夹下的权值文件,classes_path指向model_data下的txt | ||||||
|  |         # | ||||||
|  |         #   训练好后logs文件夹下存在多个权值文件,选择验证集损失较低的即可。 | ||||||
|  |         #   验证集损失较低不代表mAP较高,仅代表该权值在验证集上泛化性能较好。 | ||||||
|  |         #   如果出现shape不匹配,同时要注意训练时的onnx_path和classes_path参数的修改 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         "onnx_path"         : 'model_data/models.onnx', | ||||||
|  |         "classes_path"      : 'model_data/coco_classes.txt', | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   anchors_path代表先验框对应的txt文件,一般不修改。 | ||||||
|  |         #   anchors_mask用于帮助代码找到对应的先验框,一般不修改。 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "anchors_path"      : 'model_data/yolo_anchors.txt', | ||||||
|  |         "anchors_mask"      : [[6, 7, 8], [3, 4, 5], [0, 1, 2]], | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   输入图片的大小,必须为32的倍数。 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "input_shape"       : [640, 640], | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   只有得分大于置信度的预测框会被保留下来 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "confidence"        : 0.5, | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   非极大抑制所用到的nms_iou大小 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "nms_iou"           : 0.3, | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   该变量用于控制是否使用letterbox_image对输入图像进行不失真的resize, | ||||||
|  |         #   在多次测试后,发现关闭letterbox_image直接resize的效果更好 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "letterbox_image"   : True | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     @classmethod | ||||||
|  |     def get_defaults(cls, n): | ||||||
|  |         if n in cls._defaults: | ||||||
|  |             return cls._defaults[n] | ||||||
|  |         else: | ||||||
|  |             return "Unrecognized attribute name '" + n + "'" | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   初始化YOLO | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def __init__(self, **kwargs): | ||||||
|  |         self.__dict__.update(self._defaults) | ||||||
|  |         for name, value in kwargs.items(): | ||||||
|  |             setattr(self, name, value) | ||||||
|  |             self._defaults[name] = value  | ||||||
|  |              | ||||||
|  |         import onnxruntime | ||||||
|  |         self.onnx_session   = onnxruntime.InferenceSession(self.onnx_path) | ||||||
|  |         # 获得所有的输入node | ||||||
|  |         self.input_name     = self.get_input_name() | ||||||
|  |         # 获得所有的输出node | ||||||
|  |         self.output_name    = self.get_output_name() | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   获得种类和先验框的数量 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         self.class_names, self.num_classes  = self.get_classes(self.classes_path) | ||||||
|  |         self.anchors, self.num_anchors      = self.get_anchors(self.anchors_path) | ||||||
|  |         self.bbox_util                      = DecodeBoxNP(self.anchors, self.num_classes, (self.input_shape[0], self.input_shape[1]), self.anchors_mask) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   画框设置不同的颜色 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         hsv_tuples  = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)] | ||||||
|  |         self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) | ||||||
|  |         self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors)) | ||||||
|  |  | ||||||
|  |         show_config(**self._defaults) | ||||||
|  |   | ||||||
|  |     def get_classes(self, classes_path): | ||||||
|  |         with open(classes_path, encoding='utf-8') as f: | ||||||
|  |             class_names = f.readlines() | ||||||
|  |         class_names = [c.strip() for c in class_names] | ||||||
|  |         return class_names, len(class_names) | ||||||
|  |      | ||||||
|  |     def get_anchors(self, anchors_path): | ||||||
|  |         '''loads the anchors from a file''' | ||||||
|  |         with open(anchors_path, encoding='utf-8') as f: | ||||||
|  |             anchors = f.readline() | ||||||
|  |         anchors = [float(x) for x in anchors.split(',')] | ||||||
|  |         anchors = np.array(anchors).reshape(-1, 2) | ||||||
|  |         return anchors, len(anchors) | ||||||
|  |  | ||||||
|  |     def get_input_name(self): | ||||||
|  |         # 获得所有的输入node | ||||||
|  |         input_name=[] | ||||||
|  |         for node in self.onnx_session.get_inputs(): | ||||||
|  |             input_name.append(node.name) | ||||||
|  |         return input_name | ||||||
|  |   | ||||||
|  |     def get_output_name(self): | ||||||
|  |         # 获得所有的输出node | ||||||
|  |         output_name=[] | ||||||
|  |         for node in self.onnx_session.get_outputs(): | ||||||
|  |             output_name.append(node.name) | ||||||
|  |         return output_name | ||||||
|  |   | ||||||
|  |     def get_input_feed(self,image_tensor): | ||||||
|  |         # 利用input_name获得输入的tensor | ||||||
|  |         input_feed={} | ||||||
|  |         for name in self.input_name: | ||||||
|  |             input_feed[name]=image_tensor | ||||||
|  |         return input_feed | ||||||
|  |      | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   对输入图像进行resize | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def resize_image(self, image, size, letterbox_image, mode='PIL'): | ||||||
|  |         if mode == 'PIL': | ||||||
|  |             iw, ih  = image.size | ||||||
|  |             w, h    = size | ||||||
|  |  | ||||||
|  |             if letterbox_image: | ||||||
|  |                 scale   = min(w/iw, h/ih) | ||||||
|  |                 nw      = int(iw*scale) | ||||||
|  |                 nh      = int(ih*scale) | ||||||
|  |  | ||||||
|  |                 image   = image.resize((nw,nh), Image.BICUBIC) | ||||||
|  |                 new_image = Image.new('RGB', size, (128,128,128)) | ||||||
|  |                 new_image.paste(image, ((w-nw)//2, (h-nh)//2)) | ||||||
|  |             else: | ||||||
|  |                 new_image = image.resize((w, h), Image.BICUBIC) | ||||||
|  |         else: | ||||||
|  |             image = np.array(image) | ||||||
|  |             if letterbox_image: | ||||||
|  |                 # 获得现在的shape | ||||||
|  |                 shape       = np.shape(image)[:2] | ||||||
|  |                 # 获得输出的shape | ||||||
|  |                 if isinstance(size, int): | ||||||
|  |                     size    = (size, size) | ||||||
|  |  | ||||||
|  |                 # 计算缩放的比例 | ||||||
|  |                 r = min(size[0] / shape[0], size[1] / shape[1]) | ||||||
|  |  | ||||||
|  |                 # 计算缩放后图片的高宽 | ||||||
|  |                 new_unpad   = int(round(shape[1] * r)), int(round(shape[0] * r)) | ||||||
|  |                 dw, dh      = size[1] - new_unpad[0], size[0] - new_unpad[1] | ||||||
|  |  | ||||||
|  |                 # 除以2以padding到两边 | ||||||
|  |                 dw          /= 2   | ||||||
|  |                 dh          /= 2 | ||||||
|  |          | ||||||
|  |                 # 对图像进行resize | ||||||
|  |                 if shape[::-1] != new_unpad:  # resize | ||||||
|  |                     image = cv2.resize(image, new_unpad, interpolation=cv2.INTER_LINEAR) | ||||||
|  |                 top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) | ||||||
|  |                 left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) | ||||||
|  |          | ||||||
|  |                 new_image = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(128, 128, 128))  # add border | ||||||
|  |             else: | ||||||
|  |                 new_image = cv2.resize(image, (w, h)) | ||||||
|  |  | ||||||
|  |         return new_image | ||||||
|  |   | ||||||
|  |     def detect_image(self, image): | ||||||
|  |         image_shape = np.array(np.shape(image)[0:2]) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |   | ||||||
|  |         image_data  = self.resize_image(image, self.input_shape, True) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #   h, w, 3 => 3, h, w => 1, 3, h, w | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |   | ||||||
|  |         input_feed  = self.get_input_feed(image_data) | ||||||
|  |         outputs     = self.onnx_session.run(output_names=self.output_name, input_feed=input_feed) | ||||||
|  |  | ||||||
|  |         feature_map_shape   = [[int(j / (2 ** (i + 3))) for j in self.input_shape] for i in range(len(self.anchors_mask))][::-1] | ||||||
|  |         for i in range(len(self.anchors_mask)): | ||||||
|  |             outputs[i] = np.reshape(outputs[i], (1, len(self.anchors_mask[i]) * (5 + self.num_classes), feature_map_shape[i][0], feature_map_shape[i][1])) | ||||||
|  |          | ||||||
|  |         outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         results = self.bbox_util.non_max_suppression(np.concatenate(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                     image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou) | ||||||
|  |                                                  | ||||||
|  |         if results[0] is None:  | ||||||
|  |             return image | ||||||
|  |  | ||||||
|  |         top_label   = np.array(results[0][:, 6], dtype = 'int32') | ||||||
|  |         top_conf    = results[0][:, 4] * results[0][:, 5] | ||||||
|  |         top_boxes   = results[0][:, :4] | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   设置字体与边框厚度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         font        = ImageFont.truetype(font='model_data/simhei.ttf', size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32')) | ||||||
|  |         thickness   = int(max((image.size[0] + image.size[1]) // np.mean(self.input_shape), 1)) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   图像绘制 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         for i, c in list(enumerate(top_label)): | ||||||
|  |             predicted_class = self.class_names[int(c)] | ||||||
|  |             box             = top_boxes[i] | ||||||
|  |             score           = top_conf[i] | ||||||
|  |  | ||||||
|  |             top, left, bottom, right = box | ||||||
|  |  | ||||||
|  |             top     = max(0, np.floor(top).astype('int32')) | ||||||
|  |             left    = max(0, np.floor(left).astype('int32')) | ||||||
|  |             bottom  = min(image.size[1], np.floor(bottom).astype('int32')) | ||||||
|  |             right   = min(image.size[0], np.floor(right).astype('int32')) | ||||||
|  |  | ||||||
|  |             label = '{} {:.2f}'.format(predicted_class, score) | ||||||
|  |             draw = ImageDraw.Draw(image) | ||||||
|  |             label_size = draw.textsize(label, font) | ||||||
|  |             label = label.encode('utf-8') | ||||||
|  |             print(label, top, left, bottom, right) | ||||||
|  |              | ||||||
|  |             if top - label_size[1] >= 0: | ||||||
|  |                 text_origin = np.array([left, top - label_size[1]]) | ||||||
|  |             else: | ||||||
|  |                 text_origin = np.array([left, top + 1]) | ||||||
|  |  | ||||||
|  |             for i in range(thickness): | ||||||
|  |                 draw.rectangle([left + i, top + i, right - i, bottom - i], outline=self.colors[c]) | ||||||
|  |             draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)], fill=self.colors[c]) | ||||||
|  |             draw.text(text_origin, str(label,'UTF-8'), fill=(0, 0, 0), font=font) | ||||||
|  |             del draw | ||||||
|  |  | ||||||
|  |         return image | ||||||
							
								
								
									
										663
									
								
								yolo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										663
									
								
								yolo.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,663 @@ | |||||||
|  | import colorsys | ||||||
|  | import os | ||||||
|  | import time | ||||||
|  |  | ||||||
|  | import cv2 | ||||||
|  | import numpy as np | ||||||
|  | import torch | ||||||
|  | import torch.nn as nn | ||||||
|  | from PIL import ImageDraw, ImageFont, Image | ||||||
|  |  | ||||||
|  | from nets.yolo import YoloBody | ||||||
|  | from utils.utils import (cvtColor, get_anchors, get_classes, preprocess_input, | ||||||
|  |                          resize_image, show_config) | ||||||
|  | from utils.utils_bbox import DecodeBox, DecodeBoxNP | ||||||
|  |  | ||||||
|  | ''' | ||||||
|  | 训练自己的数据集必看注释! | ||||||
|  | ''' | ||||||
|  | class YOLO(object): | ||||||
|  |     _defaults = { | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   使用自己训练好的模型进行预测一定要修改model_path和classes_path! | ||||||
|  |         #   model_path指向logs文件夹下的权值文件,classes_path指向model_data下的txt | ||||||
|  |         # | ||||||
|  |         #   训练好后logs文件夹下存在多个权值文件,选择验证集损失较低的即可。 | ||||||
|  |         #   验证集损失较低不代表mAP较高,仅代表该权值在验证集上泛化性能较好。 | ||||||
|  |         #   如果出现shape不匹配,同时要注意训练时的model_path和classes_path参数的修改 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         "model_path"        : r'logs-yolov5\1.pth', | ||||||
|  |         "classes_path"      : 'model_data/coco_classes.txt', | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   anchors_path代表先验框对应的txt文件,一般不修改。 | ||||||
|  |         #   anchors_mask用于帮助代码找到对应的先验框,一般不修改。 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "anchors_path"      : 'model_data/yolo_anchors.txt', | ||||||
|  |         "anchors_mask"      : [[6, 7, 8], [3, 4, 5], [0, 1, 2]], | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   输入图片的大小,必须为32的倍数。 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "input_shape"       : [640, 640], | ||||||
|  |         #------------------------------------------------------# | ||||||
|  |         #   backbone        cspdarknet(默认) | ||||||
|  |         #                   convnext_tiny | ||||||
|  |         #                   convnext_small | ||||||
|  |         #                   swin_transfomer_tiny | ||||||
|  |         #------------------------------------------------------# | ||||||
|  |         "backbone"          : 'cspdarknet', | ||||||
|  |         #------------------------------------------------------# | ||||||
|  |         #   所使用的YoloV5的版本。s、m、l、x | ||||||
|  |         #   在除cspdarknet的其它主干中仅影响panet的大小 | ||||||
|  |         #------------------------------------------------------# | ||||||
|  |         "phi"               : 's', | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   只有得分大于置信度的预测框会被保留下来 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "confidence"        : 0.5, | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   非极大抑制所用到的nms_iou大小 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "nms_iou"           : 0.3, | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   该变量用于控制是否使用letterbox_image对输入图像进行不失真的resize, | ||||||
|  |         #   在多次测试后,发现关闭letterbox_image直接resize的效果更好 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "letterbox_image"   : True, | ||||||
|  |         #-------------------------------# | ||||||
|  |         #   是否使用Cuda | ||||||
|  |         #   没有GPU可以设置成False | ||||||
|  |         #-------------------------------# | ||||||
|  |         "cuda"              : True, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def get_defaults(cls, n): | ||||||
|  |         if n in cls._defaults: | ||||||
|  |             return cls._defaults[n] | ||||||
|  |         else: | ||||||
|  |             return "Unrecognized attribute name '" + n + "'" | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   初始化YOLO | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def __init__(self, **kwargs): | ||||||
|  |         self.__dict__.update(self._defaults) | ||||||
|  |         for name, value in kwargs.items(): | ||||||
|  |             setattr(self, name, value) | ||||||
|  |             self._defaults[name] = value  | ||||||
|  |              | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   获得种类和先验框的数量 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         self.class_names, self.num_classes  = get_classes(self.classes_path) | ||||||
|  |         self.anchors, self.num_anchors      = get_anchors(self.anchors_path) | ||||||
|  |         self.bbox_util                      = DecodeBox(self.anchors, self.num_classes, (self.input_shape[0], self.input_shape[1]), self.anchors_mask) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   画框设置不同的颜色 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)] | ||||||
|  |         self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) | ||||||
|  |         self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors)) | ||||||
|  |         self.generate() | ||||||
|  |  | ||||||
|  |         show_config(**self._defaults) | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   生成模型 | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def generate(self, onnx=False): | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   建立yolo模型,载入yolo模型的权重 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         self.net    = YoloBody(self.anchors_mask, self.num_classes, self.phi, backbone = self.backbone, input_shape = self.input_shape) | ||||||
|  |         device      = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | ||||||
|  |         self.net.load_state_dict(torch.load(self.model_path, map_location=device),strict=False) | ||||||
|  |         self.net    = self.net.eval() | ||||||
|  |         print('{} model, and classes loaded.'.format(self.model_path)) | ||||||
|  |         if not onnx: | ||||||
|  |             if self.cuda: | ||||||
|  |                 self.net = nn.DataParallel(self.net) | ||||||
|  |                 self.net = self.net.cuda() | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   检测图片 | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def detect_image(self, image, crop = False, count = False): | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   计算输入图片的高和宽 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         image_shape = np.array(np.shape(image)[0:2]) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   给图像增加灰条,实现不失真的resize | ||||||
|  |         #   也可以直接resize进行识别 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             images = torch.from_numpy(image_data) | ||||||
|  |             if self.cuda: | ||||||
|  |                 images = images.cuda() | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将图像输入网络当中进行预测! | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             outputs = self.net(images) | ||||||
|  |             outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                         image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou) | ||||||
|  |                                                      | ||||||
|  |             if results[0] is None:  | ||||||
|  |                 return image | ||||||
|  |  | ||||||
|  |             top_label   = np.array(results[0][:, 6], dtype = 'int32') | ||||||
|  |             top_conf    = results[0][:, 4] * results[0][:, 5] | ||||||
|  |             top_boxes   = results[0][:, :4] | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   设置字体与边框厚度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         font        = ImageFont.truetype(font='model_data/simhei.ttf', size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32')) | ||||||
|  |         thickness   = int(max((image.size[0] + image.size[1]) // np.mean(self.input_shape), 1)) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   计数 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         if count: | ||||||
|  |             print("top_label:", top_label) | ||||||
|  |             classes_nums    = np.zeros([self.num_classes]) | ||||||
|  |             for i in range(self.num_classes): | ||||||
|  |                 num = np.sum(top_label == i) | ||||||
|  |                 if num > 0: | ||||||
|  |                     print(self.class_names[i], " : ", num) | ||||||
|  |                 classes_nums[i] = num | ||||||
|  |             print("classes_nums:", classes_nums) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   是否进行目标的裁剪 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         if crop: | ||||||
|  |             for i, c in list(enumerate(top_boxes)): | ||||||
|  |                 top, left, bottom, right = top_boxes[i] | ||||||
|  |                 top     = max(0, np.floor(top).astype('int32')) | ||||||
|  |                 left    = max(0, np.floor(left).astype('int32')) | ||||||
|  |                 bottom  = min(image.size[1], np.floor(bottom).astype('int32')) | ||||||
|  |                 right   = min(image.size[0], np.floor(right).astype('int32')) | ||||||
|  |                  | ||||||
|  |                 dir_save_path = "img_crop" | ||||||
|  |                 if not os.path.exists(dir_save_path): | ||||||
|  |                     os.makedirs(dir_save_path) | ||||||
|  |                 crop_image = image.crop([left, top, right, bottom]) | ||||||
|  |                 crop_image.save(os.path.join(dir_save_path, "crop_" + str(i) + ".png"), quality=95, subsampling=0) | ||||||
|  |                 print("save crop_" + str(i) + ".png to " + dir_save_path) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   图像绘制 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         for i, c in list(enumerate(top_label)): | ||||||
|  |             predicted_class = self.class_names[int(c)] | ||||||
|  |             box             = top_boxes[i] | ||||||
|  |             score           = top_conf[i] | ||||||
|  |  | ||||||
|  |             top, left, bottom, right = box | ||||||
|  |  | ||||||
|  |             top     = max(0, np.floor(top).astype('int32')) | ||||||
|  |             left    = max(0, np.floor(left).astype('int32')) | ||||||
|  |             bottom  = min(image.size[1], np.floor(bottom).astype('int32')) | ||||||
|  |             right   = min(image.size[0], np.floor(right).astype('int32')) | ||||||
|  |  | ||||||
|  |             label = '{} {:.2f}'.format(predicted_class, score) | ||||||
|  |             draw = ImageDraw.Draw(image) | ||||||
|  |             label_size = draw.textsize(label, font) | ||||||
|  |             label = label.encode('utf-8') | ||||||
|  |             print(label, top, left, bottom, right) | ||||||
|  |              | ||||||
|  |             if top - label_size[1] >= 0: | ||||||
|  |                 text_origin = np.array([left, top - label_size[1]]) | ||||||
|  |             else: | ||||||
|  |                 text_origin = np.array([left, top + 1]) | ||||||
|  |  | ||||||
|  |             for i in range(thickness): | ||||||
|  |                 draw.rectangle([left + i, top + i, right - i, bottom - i], outline=self.colors[c]) | ||||||
|  |             draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)], fill=self.colors[c]) | ||||||
|  |             draw.text(text_origin, str(label,'UTF-8'), fill=(0, 0, 0), font=font) | ||||||
|  |             del draw | ||||||
|  |  | ||||||
|  |         return image | ||||||
|  |  | ||||||
|  |     def get_FPS(self, image, test_interval): | ||||||
|  |         image_shape = np.array(np.shape(image)[0:2]) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   给图像增加灰条,实现不失真的resize | ||||||
|  |         #   也可以直接resize进行识别 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             images = torch.from_numpy(image_data) | ||||||
|  |             if self.cuda: | ||||||
|  |                 images = images.cuda() | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将图像输入网络当中进行预测! | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             outputs = self.net(images) | ||||||
|  |             outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                         image_shape, self.letterbox_image, conf_thres=self.confidence, nms_thres=self.nms_iou) | ||||||
|  |                                                      | ||||||
|  |         t1 = time.time() | ||||||
|  |         for _ in range(test_interval): | ||||||
|  |             with torch.no_grad(): | ||||||
|  |                 #---------------------------------------------------------# | ||||||
|  |                 #   将图像输入网络当中进行预测! | ||||||
|  |                 #---------------------------------------------------------# | ||||||
|  |                 outputs = self.net(images) | ||||||
|  |                 outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |                 #---------------------------------------------------------# | ||||||
|  |                 #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |                 #---------------------------------------------------------# | ||||||
|  |                 results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                             image_shape, self.letterbox_image, conf_thres=self.confidence, nms_thres=self.nms_iou) | ||||||
|  |                              | ||||||
|  |         t2 = time.time() | ||||||
|  |         tact_time = (t2 - t1) / test_interval | ||||||
|  |         return tact_time | ||||||
|  |  | ||||||
|  |     def detect_heatmap(self, image, heatmap_save_path): | ||||||
|  |         import cv2 | ||||||
|  |         import matplotlib.pyplot as plt | ||||||
|  |         def sigmoid(x): | ||||||
|  |             y = 1.0 / (1.0 + np.exp(-x)) | ||||||
|  |             return y | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   给图像增加灰条,实现不失真的resize | ||||||
|  |         #   也可以直接resize进行识别 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = resize_image(image, (self.input_shape[1],self.input_shape[0]), self.letterbox_image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             images = torch.from_numpy(image_data) | ||||||
|  |             if self.cuda: | ||||||
|  |                 images = images.cuda() | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将图像输入网络当中进行预测! | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             outputs = self.net(images) | ||||||
|  |          | ||||||
|  |         plt.imshow(image, alpha=1) | ||||||
|  |         plt.axis('off') | ||||||
|  |         mask    = np.zeros((image.size[1], image.size[0])) | ||||||
|  |         for sub_output in outputs: | ||||||
|  |             sub_output = sub_output.cpu().numpy() | ||||||
|  |             b, c, h, w = np.shape(sub_output) | ||||||
|  |             sub_output = np.transpose(np.reshape(sub_output, [b, 3, -1, h, w]), [0, 3, 4, 1, 2])[0] | ||||||
|  |             score      = np.max(sigmoid(sub_output[..., 4]), -1) | ||||||
|  |             score      = cv2.resize(score, (image.size[0], image.size[1])) | ||||||
|  |             normed_score    = (score * 255).astype('uint8') | ||||||
|  |             mask            = np.maximum(mask, normed_score) | ||||||
|  |              | ||||||
|  |         plt.imshow(mask, alpha=0.5, interpolation='nearest', cmap="jet") | ||||||
|  |  | ||||||
|  |         plt.axis('off') | ||||||
|  |         plt.subplots_adjust(top=1, bottom=0, right=1,  left=0, hspace=0, wspace=0) | ||||||
|  |         plt.margins(0, 0) | ||||||
|  |         plt.savefig(heatmap_save_path, dpi=200, bbox_inches='tight', pad_inches = -0.1) | ||||||
|  |         print("Save to the " + heatmap_save_path) | ||||||
|  |         plt.show() | ||||||
|  |  | ||||||
|  |     def convert_to_onnx(self, simplify, model_path): | ||||||
|  |         import onnx | ||||||
|  |         self.generate(onnx=True) | ||||||
|  |  | ||||||
|  |         im                  = torch.zeros(1, 3, *self.input_shape).to('cpu')  # image size(1, 3, 512, 512) BCHW | ||||||
|  |         input_layer_names   = ["images"] | ||||||
|  |         output_layer_names  = ["output"] | ||||||
|  |          | ||||||
|  |         # Export the model | ||||||
|  |         print(f'Starting export with onnx {onnx.__version__}.') | ||||||
|  |         torch.onnx.export(self.net, | ||||||
|  |                         im, | ||||||
|  |                         f               = model_path, | ||||||
|  |                         verbose         = False, | ||||||
|  |                         opset_version   = 12, | ||||||
|  |                         training        = torch.onnx.TrainingMode.EVAL, | ||||||
|  |                         do_constant_folding = True, | ||||||
|  |                         input_names     = input_layer_names, | ||||||
|  |                         output_names    = output_layer_names, | ||||||
|  |                         dynamic_axes    = None) | ||||||
|  |  | ||||||
|  |         # Checks | ||||||
|  |         model_onnx = onnx.load(model_path)  # load onnx model | ||||||
|  |         onnx.checker.check_model(model_onnx)  # check onnx model | ||||||
|  |  | ||||||
|  |         # Simplify onnx | ||||||
|  |         if simplify: | ||||||
|  |             import onnxsim | ||||||
|  |             print(f'Simplifying with onnx-simplifier {onnxsim.__version__}.') | ||||||
|  |             model_onnx, check = onnxsim.simplify( | ||||||
|  |                 model_onnx, | ||||||
|  |                 dynamic_input_shape=False, | ||||||
|  |                 input_shapes=None) | ||||||
|  |             assert check, 'assert check failed' | ||||||
|  |             onnx.save(model_onnx, model_path) | ||||||
|  |  | ||||||
|  |         print('Onnx model save as {}'.format(model_path)) | ||||||
|  |  | ||||||
|  |     def get_map_txt(self, image_id, image, class_names, map_out_path): | ||||||
|  |         f = open(os.path.join(map_out_path, "detection-results/"+image_id+".txt"), "w", encoding='utf-8')  | ||||||
|  |         image_shape = np.array(np.shape(image)[0:2]) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   给图像增加灰条,实现不失真的resize | ||||||
|  |         #   也可以直接resize进行识别 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |  | ||||||
|  |         with torch.no_grad(): | ||||||
|  |             images = torch.from_numpy(image_data) | ||||||
|  |             if self.cuda: | ||||||
|  |                 images = images.cuda() | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将图像输入网络当中进行预测! | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             outputs = self.net(images) | ||||||
|  |             outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |             #---------------------------------------------------------# | ||||||
|  |             results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                         image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou) | ||||||
|  |                                                      | ||||||
|  |             if results[0] is None:  | ||||||
|  |                 return  | ||||||
|  |  | ||||||
|  |             top_label   = np.array(results[0][:, 6], dtype = 'int32') | ||||||
|  |             top_conf    = results[0][:, 4] * results[0][:, 5] | ||||||
|  |             top_boxes   = results[0][:, :4] | ||||||
|  |  | ||||||
|  |         for i, c in list(enumerate(top_label)): | ||||||
|  |             predicted_class = self.class_names[int(c)] | ||||||
|  |             box             = top_boxes[i] | ||||||
|  |             score           = str(top_conf[i]) | ||||||
|  |  | ||||||
|  |             top, left, bottom, right = box | ||||||
|  |             if predicted_class not in class_names: | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             f.write("%s %s %s %s %s %s\n" % (predicted_class, score[:6], str(int(left)), str(int(top)), str(int(right)),str(int(bottom)))) | ||||||
|  |  | ||||||
|  |         f.close() | ||||||
|  |         return  | ||||||
|  |  | ||||||
|  | class YOLO_ONNX(object): | ||||||
|  |     _defaults = { | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         #   使用自己训练好的模型进行预测一定要修改onnx_path和classes_path! | ||||||
|  |         #   onnx_path指向logs文件夹下的权值文件,classes_path指向model_data下的txt | ||||||
|  |         # | ||||||
|  |         #   训练好后logs文件夹下存在多个权值文件,选择验证集损失较低的即可。 | ||||||
|  |         #   验证集损失较低不代表mAP较高,仅代表该权值在验证集上泛化性能较好。 | ||||||
|  |         #   如果出现shape不匹配,同时要注意训练时的onnx_path和classes_path参数的修改 | ||||||
|  |         #--------------------------------------------------------------------------# | ||||||
|  |         "onnx_path"         : 'model_data/models.onnx', | ||||||
|  |         "classes_path"      : 'model_data/coco_classes.txt', | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   anchors_path代表先验框对应的txt文件,一般不修改。 | ||||||
|  |         #   anchors_mask用于帮助代码找到对应的先验框,一般不修改。 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "anchors_path"      : 'model_data/yolo_anchors.txt', | ||||||
|  |         "anchors_mask"      : [[6, 7, 8], [3, 4, 5], [0, 1, 2]], | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   输入图片的大小,必须为32的倍数。 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "input_shape"       : [640, 640], | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   只有得分大于置信度的预测框会被保留下来 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "confidence"        : 0.5, | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   非极大抑制所用到的nms_iou大小 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "nms_iou"           : 0.3, | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         #   该变量用于控制是否使用letterbox_image对输入图像进行不失真的resize, | ||||||
|  |         #   在多次测试后,发现关闭letterbox_image直接resize的效果更好 | ||||||
|  |         #---------------------------------------------------------------------# | ||||||
|  |         "letterbox_image"   : True | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     @classmethod | ||||||
|  |     def get_defaults(cls, n): | ||||||
|  |         if n in cls._defaults: | ||||||
|  |             return cls._defaults[n] | ||||||
|  |         else: | ||||||
|  |             return "Unrecognized attribute name '" + n + "'" | ||||||
|  |  | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   初始化YOLO | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def __init__(self, **kwargs): | ||||||
|  |         self.__dict__.update(self._defaults) | ||||||
|  |         for name, value in kwargs.items(): | ||||||
|  |             setattr(self, name, value) | ||||||
|  |             self._defaults[name] = value  | ||||||
|  |              | ||||||
|  |         import onnxruntime | ||||||
|  |         self.onnx_session   = onnxruntime.InferenceSession(self.onnx_path) | ||||||
|  |         # 获得所有的输入node | ||||||
|  |         self.input_name     = self.get_input_name() | ||||||
|  |         # 获得所有的输出node | ||||||
|  |         self.output_name    = self.get_output_name() | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   获得种类和先验框的数量 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         self.class_names, self.num_classes  = self.get_classes(self.classes_path) | ||||||
|  |         self.anchors, self.num_anchors      = self.get_anchors(self.anchors_path) | ||||||
|  |         self.bbox_util                      = DecodeBoxNP(self.anchors, self.num_classes, (self.input_shape[0], self.input_shape[1]), self.anchors_mask) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         #   画框设置不同的颜色 | ||||||
|  |         #---------------------------------------------------# | ||||||
|  |         hsv_tuples  = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)] | ||||||
|  |         self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) | ||||||
|  |         self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors)) | ||||||
|  |  | ||||||
|  |         show_config(**self._defaults) | ||||||
|  |   | ||||||
|  |     def get_classes(self, classes_path): | ||||||
|  |         with open(classes_path, encoding='utf-8') as f: | ||||||
|  |             class_names = f.readlines() | ||||||
|  |         class_names = [c.strip() for c in class_names] | ||||||
|  |         return class_names, len(class_names) | ||||||
|  |      | ||||||
|  |     def get_anchors(self, anchors_path): | ||||||
|  |         '''loads the anchors from a file''' | ||||||
|  |         with open(anchors_path, encoding='utf-8') as f: | ||||||
|  |             anchors = f.readline() | ||||||
|  |         anchors = [float(x) for x in anchors.split(',')] | ||||||
|  |         anchors = np.array(anchors).reshape(-1, 2) | ||||||
|  |         return anchors, len(anchors) | ||||||
|  |  | ||||||
|  |     def get_input_name(self): | ||||||
|  |         # 获得所有的输入node | ||||||
|  |         input_name=[] | ||||||
|  |         for node in self.onnx_session.get_inputs(): | ||||||
|  |             input_name.append(node.name) | ||||||
|  |         return input_name | ||||||
|  |   | ||||||
|  |     def get_output_name(self): | ||||||
|  |         # 获得所有的输出node | ||||||
|  |         output_name=[] | ||||||
|  |         for node in self.onnx_session.get_outputs(): | ||||||
|  |             output_name.append(node.name) | ||||||
|  |         return output_name | ||||||
|  |   | ||||||
|  |     def get_input_feed(self,image_tensor): | ||||||
|  |         # 利用input_name获得输入的tensor | ||||||
|  |         input_feed={} | ||||||
|  |         for name in self.input_name: | ||||||
|  |             input_feed[name]=image_tensor | ||||||
|  |         return input_feed | ||||||
|  |      | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     #   对输入图像进行resize | ||||||
|  |     #---------------------------------------------------# | ||||||
|  |     def resize_image(self, image, size, letterbox_image, mode='PIL'): | ||||||
|  |         if mode == 'PIL': | ||||||
|  |             iw, ih  = image.size | ||||||
|  |             w, h    = size | ||||||
|  |  | ||||||
|  |             if letterbox_image: | ||||||
|  |                 scale   = min(w/iw, h/ih) | ||||||
|  |                 nw      = int(iw*scale) | ||||||
|  |                 nh      = int(ih*scale) | ||||||
|  |  | ||||||
|  |                 image   = image.resize((nw,nh), Image.BICUBIC) | ||||||
|  |                 new_image = Image.new('RGB', size, (128,128,128)) | ||||||
|  |                 new_image.paste(image, ((w-nw)//2, (h-nh)//2)) | ||||||
|  |             else: | ||||||
|  |                 new_image = image.resize((w, h), Image.BICUBIC) | ||||||
|  |         else: | ||||||
|  |             image = np.array(image) | ||||||
|  |             if letterbox_image: | ||||||
|  |                 # 获得现在的shape | ||||||
|  |                 shape       = np.shape(image)[:2] | ||||||
|  |                 # 获得输出的shape | ||||||
|  |                 if isinstance(size, int): | ||||||
|  |                     size    = (size, size) | ||||||
|  |  | ||||||
|  |                 # 计算缩放的比例 | ||||||
|  |                 r = min(size[0] / shape[0], size[1] / shape[1]) | ||||||
|  |  | ||||||
|  |                 # 计算缩放后图片的高宽 | ||||||
|  |                 new_unpad   = int(round(shape[1] * r)), int(round(shape[0] * r)) | ||||||
|  |                 dw, dh      = size[1] - new_unpad[0], size[0] - new_unpad[1] | ||||||
|  |  | ||||||
|  |                 # 除以2以padding到两边 | ||||||
|  |                 dw          /= 2   | ||||||
|  |                 dh          /= 2 | ||||||
|  |          | ||||||
|  |                 # 对图像进行resize | ||||||
|  |                 if shape[::-1] != new_unpad:  # resize | ||||||
|  |                     image = cv2.resize(image, new_unpad, interpolation=cv2.INTER_LINEAR) | ||||||
|  |                 top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) | ||||||
|  |                 left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) | ||||||
|  |          | ||||||
|  |                 new_image = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(128, 128, 128))  # add border | ||||||
|  |             else: | ||||||
|  |                 new_image = cv2.resize(image, (w, h)) | ||||||
|  |  | ||||||
|  |         return new_image | ||||||
|  |   | ||||||
|  |     def detect_image(self, image): | ||||||
|  |         image_shape = np.array(np.shape(image)[0:2]) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。 | ||||||
|  |         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image       = cvtColor(image) | ||||||
|  |   | ||||||
|  |         image_data  = self.resize_image(image, self.input_shape, True) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   添加上batch_size维度 | ||||||
|  |         #   h, w, 3 => 3, h, w => 1, 3, h, w | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) | ||||||
|  |   | ||||||
|  |         input_feed  = self.get_input_feed(image_data) | ||||||
|  |         outputs     = self.onnx_session.run(output_names=self.output_name, input_feed=input_feed) | ||||||
|  |  | ||||||
|  |         feature_map_shape   = [[int(j / (2 ** (i + 3))) for j in self.input_shape] for i in range(len(self.anchors_mask))][::-1] | ||||||
|  |         for i in range(len(self.anchors_mask)): | ||||||
|  |             outputs[i] = np.reshape(outputs[i], (1, len(self.anchors_mask[i]) * (5 + self.num_classes), feature_map_shape[i][0], feature_map_shape[i][1])) | ||||||
|  |          | ||||||
|  |         outputs = self.bbox_util.decode_box(outputs) | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   将预测框进行堆叠,然后进行非极大抑制 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         results = self.bbox_util.non_max_suppression(np.concatenate(outputs, 1), self.num_classes, self.input_shape,  | ||||||
|  |                     image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou) | ||||||
|  |                                                  | ||||||
|  |         if results[0] is None:  | ||||||
|  |             return image | ||||||
|  |  | ||||||
|  |         top_label   = np.array(results[0][:, 6], dtype = 'int32') | ||||||
|  |         top_conf    = results[0][:, 4] * results[0][:, 5] | ||||||
|  |         top_boxes   = results[0][:, :4] | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   设置字体与边框厚度 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         font        = ImageFont.truetype(font='model_data/simhei.ttf', size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32')) | ||||||
|  |         thickness   = int(max((image.size[0] + image.size[1]) // np.mean(self.input_shape), 1)) | ||||||
|  |  | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         #   图像绘制 | ||||||
|  |         #---------------------------------------------------------# | ||||||
|  |         for i, c in list(enumerate(top_label)): | ||||||
|  |             predicted_class = self.class_names[int(c)] | ||||||
|  |             box             = top_boxes[i] | ||||||
|  |             score           = top_conf[i] | ||||||
|  |  | ||||||
|  |             top, left, bottom, right = box | ||||||
|  |  | ||||||
|  |             top     = max(0, np.floor(top).astype('int32')) | ||||||
|  |             left    = max(0, np.floor(left).astype('int32')) | ||||||
|  |             bottom  = min(image.size[1], np.floor(bottom).astype('int32')) | ||||||
|  |             right   = min(image.size[0], np.floor(right).astype('int32')) | ||||||
|  |  | ||||||
|  |             label = '{} {:.2f}'.format(predicted_class, score) | ||||||
|  |             draw = ImageDraw.Draw(image) | ||||||
|  |             label_size = draw.textsize(label, font) | ||||||
|  |             label = label.encode('utf-8') | ||||||
|  |             print(label, top, left, bottom, right) | ||||||
|  |              | ||||||
|  |             if top - label_size[1] >= 0: | ||||||
|  |                 text_origin = np.array([left, top - label_size[1]]) | ||||||
|  |             else: | ||||||
|  |                 text_origin = np.array([left, top + 1]) | ||||||
|  |  | ||||||
|  |             for i in range(thickness): | ||||||
|  |                 draw.rectangle([left + i, top + i, right - i, bottom - i], outline=self.colors[c]) | ||||||
|  |             draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)], fill=self.colors[c]) | ||||||
|  |             draw.text(text_origin, str(label,'UTF-8'), fill=(0, 0, 0), font=font) | ||||||
|  |             del draw | ||||||
|  |  | ||||||
|  |         return image | ||||||
		Reference in New Issue
	
	Block a user