├── models ├── __init__.py ├── hub │ ├── yolov5-fpn.yaml │ ├── yolov3-tiny.yaml │ ├── yolov5-p34.yaml │ ├── yolov5-panet.yaml │ ├── yolov5-bifpn.yaml │ ├── yolov5s-transformer.yaml │ ├── yolov5s-ghost.yaml │ ├── yolov3.yaml │ ├── yolov3-spp.yaml │ ├── yolov5-p2.yaml │ ├── yolov5-p6.yaml │ ├── yolov5l6.yaml │ ├── yolov5m6.yaml │ ├── yolov5n6.yaml │ ├── yolov5s6.yaml │ ├── yolov5x6.yaml │ ├── yolov5-p7.yaml │ └── anchors.yaml ├── yolov5l.yaml ├── yolov5m.yaml ├── yolov5n.yaml ├── yolov5s.yaml ├── yolov5x.yaml └── experimental.py ├── utils ├── loggers │ ├── wandb │ │ ├── __init__.py │ │ ├── log_dataset.py │ │ ├── sweep.py │ │ └── sweep.yaml │ └── __init__.py ├── autobatch.py └── activations.py ├── tools ├── process_labels │ ├── remove_object.py │ ├── noise_missing.py │ ├── noise_lable.py │ ├── remove_object_on_tomato.py │ ├── remove_object_on_paprika.py │ ├── Json2xml.py │ ├── Json2xml_.py │ ├── Json2xml_paprika.py │ ├── Json2xml_strawberry.py │ ├── Json2xml_tomoto.py │ ├── merge_box.py │ ├── noise_position.py │ ├── noise_size.py │ ├── calculate_bounding_box.py │ ├── noise_concat_using_xmlfile.py │ └── noise_concat.py ├── process_images │ ├── move_correct_images.py │ ├── modify_orientation_mpo.py │ ├── modify_orientation_jpeg.py │ └── split_the_datasets.py ├── show_xml │ ├── show_xml_on2500.py │ ├── show_xml_on_tomato.py │ ├── show_xml_on_paprika.py │ └── show_xml_on_resultimage.py └── data_augmentation │ ├── data_aug_paprika.py │ └── data_aug.py ├── weights └── Readme.txt ├── datasets └── Readme.txt ├── data ├── paprika_v4.yaml ├── hyps │ ├── hyp.finetune_objects365.yaml │ ├── hyp.finetune.yaml │ ├── hyp.scratch.yaml │ ├── hyp.scratch-high.yaml │ ├── hyp.scratch-med.yaml │ └── hyp.scratch-low.yaml ├── tomato.yaml └── strawberry_v4.yaml ├── requirements.txt ├── setup.cfg ├── Dockerfile ├── README.md ├── EigenCAM.py ├── CONTRIBUTING.md └── hubconf.py /models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/loggers/wandb/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/process_labels/remove_object.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weights/Readme.txt: -------------------------------------------------------------------------------- 1 | YOLO * .pt is the pre-trained model of COCO dataset. 2 | paprika is the pre-trained model of paprika dataset. -------------------------------------------------------------------------------- /datasets/Readme.txt: -------------------------------------------------------------------------------- 1 | Please put your datasets here. 2 | 3 | You can refer to the official settings of YOLO-v5. 4 | Here is the link: https://github.com/ultralytics/yolov5 -------------------------------------------------------------------------------- /data/paprika_v4.yaml: -------------------------------------------------------------------------------- 1 | # 标签最好的。 2 | 3 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 4 | path: /home/multiai3/Jiuqing/yolo5-official-new/datasets/paprika_v4 # dataset root dir 5 | train: images/train # train images (relative to 'path') 128 images 6 | val: images/val # val images (relative to 'path') 128 images 7 | test: images/test # test images (optional) 8 | # Classes 9 | nc: 5 10 | # class names 11 | names: ["blossom_end_rot", "graymold", "powdery_mildew", "spider_mite","spotting_disease"] -------------------------------------------------------------------------------- /data/hyps/hyp.finetune_objects365.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | lr0: 0.00258 4 | lrf: 0.17 5 | momentum: 0.779 6 | weight_decay: 0.00058 7 | warmup_epochs: 1.33 8 | warmup_momentum: 0.86 9 | warmup_bias_lr: 0.0711 10 | box: 0.0539 11 | cls: 0.299 12 | cls_pw: 0.825 13 | obj: 0.632 14 | obj_pw: 1.0 15 | iou_t: 0.2 16 | anchor_t: 3.44 17 | anchors: 3.2 18 | fl_gamma: 0.0 19 | hsv_h: 0.0188 20 | hsv_s: 0.704 21 | hsv_v: 0.36 22 | degrees: 0.0 23 | translate: 0.0902 24 | scale: 0.491 25 | shear: 0.0 26 | perspective: 0.0 27 | flipud: 0.0 28 | fliplr: 0.5 29 | mosaic: 1.0 30 | mixup: 0.0 31 | copy_paste: 0.0 32 | -------------------------------------------------------------------------------- /data/tomato.yaml: -------------------------------------------------------------------------------- 1 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 2 | path: /home/multiai3/Jiuqing/yolo5-official-new/datasets/tomato # dataset root dir 3 | train: images/train # train images (relative to 'path') 128 images 4 | val: images/val # val images (relative to 'path') 128 images 5 | test: images/test # test images (optional) 6 | 7 | # Classes 8 | nc: 19 9 | 10 | # class names 11 | names: ["healthy", "canker", "plague", "back", "yculr", "miner", "lmold", "tocv", "stress", "powder", 12 | "gmold", "wfly", "eggfly", "death", "spot","old", "magdef", "unknown", "wilt"] -------------------------------------------------------------------------------- /tools/process_images/move_correct_images.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | #project = 'strawberry_bigbox_v5' 4 | project = 'paprika_semi_global/x' 5 | 6 | detect_path = '/home/multiai3/Jiuqing/yolo5-official-new/'+project+'/detect' 7 | txt_path = '/home/multiai3/Jiuqing/yolo5-official-new/'+project+'/detect_wrong.txt' 8 | 9 | with open(txt_path, 'r') as wrong_list: 10 | wrong = wrong_list.read().splitlines() 11 | wrong_path = detect_path+'_wrong' 12 | 13 | if not os.path.exists(wrong_path): 14 | os.mkdir(wrong_path) 15 | i = 0 16 | for root, dirs, files in os.walk(detect_path): 17 | for file in files: 18 | if file in wrong: 19 | src = os.path.join(root,file) 20 | shutil.move(src, wrong_path) 21 | i+=1 22 | print(i) 23 | -------------------------------------------------------------------------------- /data/strawberry_v4.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # COCO128 dataset https://www.kaggle.com/ultralytics/coco128 (first 128 images from COCO train2017) by Ultralytics 3 | # Example usage: python train.py --data coco128.yaml 4 | # parent 5 | # ├── yolov5 6 | # └── datasets 7 | # └── coco128 ← downloads here 8 | # 这个是大框的第二版。调整了powder middle leaf的框 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: /home/multiai3/Jiuqing/yolo5-official-new/datasets/strawberry_v4 # dataset root dir 12 | train: images/train # train images (relative to 'path') 128 images 13 | val: images/val # val images (relative to 'path') 128 images 14 | test: images/test # test images (optional) 15 | 16 | # Classes 17 | nc: 8 18 | 19 | # class names 20 | names: ["powdery_mildew_leaf", "angular_leafspot", "anthracnose_fruit_rot", "anthracnose_runner", 21 | "blossom_blight", "gray_mold", "leaf_spot", "powdery_mildew_fruit"] -------------------------------------------------------------------------------- /data/hyps/hyp.finetune.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for VOC finetuning 3 | # python train.py --batch 64 --weights yolov5m.pt --data VOC.yaml --img 512 --epochs 50 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | # Hyperparameter Evolution Results 7 | # Generations: 306 8 | # P R mAP.5 mAP.5:.95 box obj cls 9 | # Metrics: 0.6 0.936 0.896 0.684 0.0115 0.00805 0.00146 10 | 11 | lr0: 0.0032 12 | lrf: 0.12 13 | momentum: 0.843 14 | weight_decay: 0.00036 15 | warmup_epochs: 2.0 16 | warmup_momentum: 0.5 17 | warmup_bias_lr: 0.05 18 | box: 0.0296 19 | cls: 0.243 20 | cls_pw: 0.631 21 | obj: 0.301 22 | obj_pw: 0.911 23 | iou_t: 0.2 24 | anchor_t: 2.91 25 | # anchors: 3.63 26 | fl_gamma: 0.0 27 | hsv_h: 0.0138 28 | hsv_s: 0.664 29 | hsv_v: 0.464 30 | degrees: 0.373 31 | translate: 0.245 32 | scale: 0.898 33 | shear: 0.602 34 | perspective: 0.0 35 | flipud: 0.00856 36 | fliplr: 0.5 37 | mosaic: 1.0 38 | mixup: 0.243 39 | copy_paste: 0.0 40 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements.txt 2 | 3 | # Base ---------------------------------------- 4 | matplotlib>=3.2.2 5 | numpy>=1.18.5 6 | opencv-python>=4.1.2 7 | Pillow>=7.1.2 8 | PyYAML>=5.3.1 9 | requests>=2.23.0 10 | scipy>=1.4.1 11 | torch>=1.7.0 12 | torchvision>=0.8.1 13 | tqdm>=4.41.0 14 | 15 | # Logging ------------------------------------- 16 | tensorboard>=2.4.1 17 | # wandb 18 | 19 | # Plotting ------------------------------------ 20 | pandas>=1.1.4 21 | seaborn>=0.11.0 22 | 23 | # Export -------------------------------------- 24 | # coremltools>=4.1 # CoreML export 25 | # onnx>=1.9.0 # ONNX export 26 | # onnx-simplifier>=0.3.6 # ONNX simplifier 27 | # scikit-learn==0.19.2 # CoreML quantization 28 | # tensorflow>=2.4.1 # TFLite export 29 | # tensorflowjs>=3.9.0 # TF.js export 30 | # openvino-dev # OpenVINO export 31 | 32 | # Extras -------------------------------------- 33 | # albumentations>=1.0.3 34 | # Cython # for pycocotools https://github.com/cocodataset/cocoapi/issues/172 35 | # pycocotools>=2.0 # COCO mAP 36 | # roboflow 37 | thop # FLOPs computation 38 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # Project-wide configuration file, can be used for package metadata and other toll configurations 2 | # Example usage: global configuration for PEP8 (via flake8) setting or default pytest arguments 3 | 4 | [metadata] 5 | license_file = LICENSE 6 | description-file = README.md 7 | 8 | 9 | [tool:pytest] 10 | norecursedirs = 11 | .git 12 | dist 13 | build 14 | addopts = 15 | --doctest-modules 16 | --durations=25 17 | --color=yes 18 | 19 | 20 | [flake8] 21 | max-line-length = 120 22 | exclude = .tox,*.egg,build,temp 23 | select = E,W,F 24 | doctests = True 25 | verbose = 2 26 | # https://pep8.readthedocs.io/en/latest/intro.html#error-codes 27 | format = pylint 28 | # see: https://www.flake8rules.com/ 29 | ignore = 30 | E731 # Do not assign a lambda expression, use a def 31 | F405 32 | E402 33 | F841 34 | E741 35 | F821 36 | E722 37 | F401 38 | W504 39 | E127 40 | W504 41 | E231 42 | E501 43 | F403 44 | E302 45 | F541 46 | 47 | 48 | [isort] 49 | # https://pycqa.github.io/isort/docs/configuration/options.html 50 | line_length = 120 51 | multi_line_output = 0 52 | -------------------------------------------------------------------------------- /utils/loggers/wandb/log_dataset.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from wandb_utils import WandbLogger 4 | 5 | from utils.general import LOGGER 6 | 7 | WANDB_ARTIFACT_PREFIX = 'wandb-artifact://' 8 | 9 | 10 | def create_dataset_artifact(opt): 11 | logger = WandbLogger(opt, None, job_type='Dataset Creation') # TODO: return value unused 12 | if not logger.wandb: 13 | LOGGER.info("install wandb using `pip install wandb` to log the dataset") 14 | 15 | 16 | if __name__ == '__main__': 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument('--data', type=str, default='data/coco128.yaml', help='data.yaml path') 19 | parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset') 20 | parser.add_argument('--project', type=str, default='YOLOv5', help='name of W&B Project') 21 | parser.add_argument('--entity', default=None, help='W&B entity') 22 | parser.add_argument('--name', type=str, default='log dataset', help='name of W&B run') 23 | 24 | opt = parser.parse_args() 25 | opt.resume = False # Explicitly disallow resume check for dataset upload job 26 | 27 | create_dataset_artifact(opt) 28 | -------------------------------------------------------------------------------- /utils/loggers/wandb/sweep.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import wandb 5 | 6 | FILE = Path(__file__).resolve() 7 | ROOT = FILE.parents[3] # YOLOv5 root directory 8 | if str(ROOT) not in sys.path: 9 | sys.path.append(str(ROOT)) # add ROOT to PATH 10 | 11 | from train import parse_opt, train 12 | from utils.callbacks import Callbacks 13 | from utils.general import increment_path 14 | from utils.torch_utils import select_device 15 | 16 | 17 | def sweep(): 18 | wandb.init() 19 | # Get hyp dict from sweep agent 20 | hyp_dict = vars(wandb.config).get("_items") 21 | 22 | # Workaround: get necessary opt args 23 | opt = parse_opt(known=True) 24 | opt.batch_size = hyp_dict.get("batch_size") 25 | opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok or opt.evolve)) 26 | opt.epochs = hyp_dict.get("epochs") 27 | opt.nosave = True 28 | opt.data = hyp_dict.get("data") 29 | opt.weights = str(opt.weights) 30 | opt.cfg = str(opt.cfg) 31 | opt.data = str(opt.data) 32 | opt.hyp = str(opt.hyp) 33 | opt.project = str(opt.project) 34 | device = select_device(opt.device, batch_size=opt.batch_size) 35 | 36 | # train 37 | train(hyp_dict, opt, device, callbacks=Callbacks()) 38 | 39 | 40 | if __name__ == "__main__": 41 | sweep() 42 | -------------------------------------------------------------------------------- /models/hub/yolov5-fpn.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 FPN head 28 | head: 29 | [[-1, 3, C3, [1024, False]], # 10 (P5/32-large) 30 | 31 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 32 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 33 | [-1, 1, Conv, [512, 1, 1]], 34 | [-1, 3, C3, [512, False]], # 14 (P4/16-medium) 35 | 36 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 37 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 38 | [-1, 1, Conv, [256, 1, 1]], 39 | [-1, 3, C3, [256, False]], # 18 (P3/8-small) 40 | 41 | [[18, 14, 10], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 42 | ] 43 | -------------------------------------------------------------------------------- /models/hub/yolov3-tiny.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 8 | - [10,14, 23,27, 37,58] # P4/16 9 | - [81,82, 135,169, 344,319] # P5/32 10 | 11 | # YOLOv3-tiny backbone 12 | backbone: 13 | # [from, number, module, args] 14 | [[-1, 1, Conv, [16, 3, 1]], # 0 15 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 1-P1/2 16 | [-1, 1, Conv, [32, 3, 1]], 17 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 3-P2/4 18 | [-1, 1, Conv, [64, 3, 1]], 19 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 5-P3/8 20 | [-1, 1, Conv, [128, 3, 1]], 21 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 7-P4/16 22 | [-1, 1, Conv, [256, 3, 1]], 23 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 9-P5/32 24 | [-1, 1, Conv, [512, 3, 1]], 25 | [-1, 1, nn.ZeroPad2d, [[0, 1, 0, 1]]], # 11 26 | [-1, 1, nn.MaxPool2d, [2, 1, 0]], # 12 27 | ] 28 | 29 | # YOLOv3-tiny head 30 | head: 31 | [[-1, 1, Conv, [1024, 3, 1]], 32 | [-1, 1, Conv, [256, 1, 1]], 33 | [-1, 1, Conv, [512, 3, 1]], # 15 (P5/32-large) 34 | 35 | [-2, 1, Conv, [128, 1, 1]], 36 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 37 | [[-1, 8], 1, Concat, [1]], # cat backbone P4 38 | [-1, 1, Conv, [256, 3, 1]], # 19 (P4/16-medium) 39 | 40 | [[19, 15], 1, Detect, [nc, anchors]], # Detect(P4, P5) 41 | ] 42 | -------------------------------------------------------------------------------- /models/hub/yolov5-p34.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 0.33 # model depth multiple 6 | width_multiple: 0.50 # layer channel multiple 7 | anchors: 3 # AutoAnchor evolves 3 anchors per P output layer 8 | 9 | # YOLOv5 v6.0 backbone 10 | backbone: 11 | # [from, number, module, args] 12 | [ [ -1, 1, Conv, [ 64, 6, 2, 2 ] ], # 0-P1/2 13 | [ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4 14 | [ -1, 3, C3, [ 128 ] ], 15 | [ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8 16 | [ -1, 6, C3, [ 256 ] ], 17 | [ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16 18 | [ -1, 9, C3, [ 512 ] ], 19 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], # 7-P5/32 20 | [ -1, 3, C3, [ 1024 ] ], 21 | [ -1, 1, SPPF, [ 1024, 5 ] ], # 9 22 | ] 23 | 24 | # YOLOv5 v6.0 head with (P3, P4) outputs 25 | head: 26 | [ [ -1, 1, Conv, [ 512, 1, 1 ] ], 27 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 28 | [ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4 29 | [ -1, 3, C3, [ 512, False ] ], # 13 30 | 31 | [ -1, 1, Conv, [ 256, 1, 1 ] ], 32 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 33 | [ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3 34 | [ -1, 3, C3, [ 256, False ] ], # 17 (P3/8-small) 35 | 36 | [ -1, 1, Conv, [ 256, 3, 2 ] ], 37 | [ [ -1, 14 ], 1, Concat, [ 1 ] ], # cat head P4 38 | [ -1, 3, C3, [ 512, False ] ], # 20 (P4/16-medium) 39 | 40 | [ [ 17, 20 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4) 41 | ] 42 | -------------------------------------------------------------------------------- /models/yolov5l.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 5 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, C3, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/yolov5m.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 8 # number of classes 5 | depth_multiple: 0.67 # model depth multiple 6 | width_multiple: 0.75 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, C3, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/yolov5n.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 8 # number of classes 5 | depth_multiple: 0.33 # model depth multiple 6 | width_multiple: 0.25 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, C3, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/yolov5s.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 19 # number of classes 5 | depth_multiple: 0.33 # model depth multiple 6 | width_multiple: 0.50 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, C3, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/yolov5x.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 19 # number of classes 5 | depth_multiple: 1.33 # model depth multiple 6 | width_multiple: 1.25 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, C3, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/hub/yolov5-panet.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 PANet head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, C3, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/hub/yolov5-bifpn.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 BiFPN head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14, 6], 1, Concat, [1]], # cat P4 <--- BiFPN change 41 | [-1, 3, C3, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/hub/yolov5s-transformer.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 0.33 # model depth multiple 6 | width_multiple: 0.50 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3TR, [1024]], # 9 <--- C3TR() Transformer module 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, C3, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/hub/yolov5s-ghost.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 0.33 # model depth multiple 6 | width_multiple: 0.50 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 v6.0 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 16 | [-1, 1, GhostConv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, C3Ghost, [128]], 18 | [-1, 1, GhostConv, [256, 3, 2]], # 3-P3/8 19 | [-1, 6, C3Ghost, [256]], 20 | [-1, 1, GhostConv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3Ghost, [512]], 22 | [-1, 1, GhostConv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 3, C3Ghost, [1024]], 24 | [-1, 1, SPPF, [1024, 5]], # 9 25 | ] 26 | 27 | # YOLOv5 v6.0 head 28 | head: 29 | [[-1, 1, GhostConv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, C3Ghost, [512, False]], # 13 33 | 34 | [-1, 1, GhostConv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, C3Ghost, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, GhostConv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, C3Ghost, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, GhostConv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, C3Ghost, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/hub/yolov3.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # darknet53 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [32, 3, 1]], # 0 16 | [-1, 1, Conv, [64, 3, 2]], # 1-P1/2 17 | [-1, 1, Bottleneck, [64]], 18 | [-1, 1, Conv, [128, 3, 2]], # 3-P2/4 19 | [-1, 2, Bottleneck, [128]], 20 | [-1, 1, Conv, [256, 3, 2]], # 5-P3/8 21 | [-1, 8, Bottleneck, [256]], 22 | [-1, 1, Conv, [512, 3, 2]], # 7-P4/16 23 | [-1, 8, Bottleneck, [512]], 24 | [-1, 1, Conv, [1024, 3, 2]], # 9-P5/32 25 | [-1, 4, Bottleneck, [1024]], # 10 26 | ] 27 | 28 | # YOLOv3 head 29 | head: 30 | [[-1, 1, Bottleneck, [1024, False]], 31 | [-1, 1, Conv, [512, [1, 1]]], 32 | [-1, 1, Conv, [1024, 3, 1]], 33 | [-1, 1, Conv, [512, 1, 1]], 34 | [-1, 1, Conv, [1024, 3, 1]], # 15 (P5/32-large) 35 | 36 | [-2, 1, Conv, [256, 1, 1]], 37 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 38 | [[-1, 8], 1, Concat, [1]], # cat backbone P4 39 | [-1, 1, Bottleneck, [512, False]], 40 | [-1, 1, Bottleneck, [512, False]], 41 | [-1, 1, Conv, [256, 1, 1]], 42 | [-1, 1, Conv, [512, 3, 1]], # 22 (P4/16-medium) 43 | 44 | [-2, 1, Conv, [128, 1, 1]], 45 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 46 | [[-1, 6], 1, Concat, [1]], # cat backbone P3 47 | [-1, 1, Bottleneck, [256, False]], 48 | [-1, 2, Bottleneck, [256, False]], # 27 (P3/8-small) 49 | 50 | [[27, 22, 15], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 51 | ] 52 | -------------------------------------------------------------------------------- /models/hub/yolov3-spp.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # darknet53 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [32, 3, 1]], # 0 16 | [-1, 1, Conv, [64, 3, 2]], # 1-P1/2 17 | [-1, 1, Bottleneck, [64]], 18 | [-1, 1, Conv, [128, 3, 2]], # 3-P2/4 19 | [-1, 2, Bottleneck, [128]], 20 | [-1, 1, Conv, [256, 3, 2]], # 5-P3/8 21 | [-1, 8, Bottleneck, [256]], 22 | [-1, 1, Conv, [512, 3, 2]], # 7-P4/16 23 | [-1, 8, Bottleneck, [512]], 24 | [-1, 1, Conv, [1024, 3, 2]], # 9-P5/32 25 | [-1, 4, Bottleneck, [1024]], # 10 26 | ] 27 | 28 | # YOLOv3-SPP head 29 | head: 30 | [[-1, 1, Bottleneck, [1024, False]], 31 | [-1, 1, SPP, [512, [5, 9, 13]]], 32 | [-1, 1, Conv, [1024, 3, 1]], 33 | [-1, 1, Conv, [512, 1, 1]], 34 | [-1, 1, Conv, [1024, 3, 1]], # 15 (P5/32-large) 35 | 36 | [-2, 1, Conv, [256, 1, 1]], 37 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 38 | [[-1, 8], 1, Concat, [1]], # cat backbone P4 39 | [-1, 1, Bottleneck, [512, False]], 40 | [-1, 1, Bottleneck, [512, False]], 41 | [-1, 1, Conv, [256, 1, 1]], 42 | [-1, 1, Conv, [512, 3, 1]], # 22 (P4/16-medium) 43 | 44 | [-2, 1, Conv, [128, 1, 1]], 45 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 46 | [[-1, 6], 1, Concat, [1]], # cat backbone P3 47 | [-1, 1, Bottleneck, [256, False]], 48 | [-1, 2, Bottleneck, [256, False]], # 27 (P3/8-small) 49 | 50 | [[27, 22, 15], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 51 | ] 52 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for COCO training from scratch 3 | # python train.py --batch 40 --cfg yolov5m.yaml --weights '' --data coco.yaml --img 640 --epochs 300 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) 7 | lrf: 0.1 # final OneCycleLR learning rate (lr0 * lrf) 8 | momentum: 0.937 # SGD momentum/Adam beta1 9 | weight_decay: 0.0005 # optimizer weight decay 5e-4 10 | warmup_epochs: 3.0 # warmup epochs (fractions ok) 11 | warmup_momentum: 0.8 # warmup initial momentum 12 | warmup_bias_lr: 0.1 # warmup initial bias lr 13 | box: 0.05 # box loss gain 14 | cls: 0.5 # cls loss gain 15 | cls_pw: 1.0 # cls BCELoss positive_weight 16 | obj: 1.0 # obj loss gain (scale with pixels) 17 | obj_pw: 1.0 # obj BCELoss positive_weight 18 | iou_t: 0.20 # IoU training threshold 19 | anchor_t: 4.0 # anchor-multiple threshold 20 | # anchors: 3 # anchors per output layer (0 to ignore) 21 | fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) 22 | hsv_h: 0.015 # image HSV-Hue augmentation (fraction) 23 | hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) 24 | hsv_v: 0.4 # image HSV-Value augmentation (fraction) 25 | degrees: 0.0 # image rotation (+/- deg) 26 | translate: 0.1 # image translation (+/- fraction) 27 | scale: 0.5 # image scale (+/- gain) 28 | shear: 0.0 # image shear (+/- deg) 29 | perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 30 | flipud: 0.0 # image flip up-down (probability) 31 | fliplr: 0.5 # image flip left-right (probability) 32 | mosaic: 1.0 # image mosaic (probability) 33 | mixup: 0.0 # image mixup (probability) 34 | copy_paste: 0.0 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-high.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for high-augmentation COCO training from scratch 3 | # python train.py --batch 32 --cfg yolov5m6.yaml --weights '' --data coco.yaml --img 1280 --epochs 300 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) 7 | lrf: 0.2 # final OneCycleLR learning rate (lr0 * lrf) 8 | momentum: 0.937 # SGD momentum/Adam beta1 9 | weight_decay: 0.0005 # optimizer weight decay 5e-4 10 | warmup_epochs: 3.0 # warmup epochs (fractions ok) 11 | warmup_momentum: 0.8 # warmup initial momentum 12 | warmup_bias_lr: 0.1 # warmup initial bias lr 13 | box: 0.05 # box loss gain 14 | cls: 0.3 # cls loss gain 15 | cls_pw: 1.0 # cls BCELoss positive_weight 16 | obj: 0.7 # obj loss gain (scale with pixels) 17 | obj_pw: 1.0 # obj BCELoss positive_weight 18 | iou_t: 0.20 # IoU training threshold 19 | anchor_t: 4.0 # anchor-multiple threshold 20 | # anchors: 3 # anchors per output layer (0 to ignore) 21 | fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) 22 | hsv_h: 0.015 # image HSV-Hue augmentation (fraction) 23 | hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) 24 | hsv_v: 0.4 # image HSV-Value augmentation (fraction) 25 | degrees: 0.0 # image rotation (+/- deg) 26 | translate: 0.1 # image translation (+/- fraction) 27 | scale: 0.9 # image scale (+/- gain) 28 | shear: 0.0 # image shear (+/- deg) 29 | perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 30 | flipud: 0.0 # image flip up-down (probability) 31 | fliplr: 0.5 # image flip left-right (probability) 32 | mosaic: 1.0 # image mosaic (probability) 33 | mixup: 0.1 # image mixup (probability) 34 | copy_paste: 0.1 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-med.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for medium-augmentation COCO training from scratch 3 | # python train.py --batch 32 --cfg yolov5m6.yaml --weights '' --data coco.yaml --img 1280 --epochs 300 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) 7 | lrf: 0.1 # final OneCycleLR learning rate (lr0 * lrf) 8 | momentum: 0.937 # SGD momentum/Adam beta1 9 | weight_decay: 0.0005 # optimizer weight decay 5e-4 10 | warmup_epochs: 3.0 # warmup epochs (fractions ok) 11 | warmup_momentum: 0.8 # warmup initial momentum 12 | warmup_bias_lr: 0.1 # warmup initial bias lr 13 | box: 0.05 # box loss gain 14 | cls: 0.3 # cls loss gain 15 | cls_pw: 1.0 # cls BCELoss positive_weight 16 | obj: 0.7 # obj loss gain (scale with pixels) 17 | obj_pw: 1.0 # obj BCELoss positive_weight 18 | iou_t: 0.20 # IoU training threshold 19 | anchor_t: 4.0 # anchor-multiple threshold 20 | # anchors: 3 # anchors per output layer (0 to ignore) 21 | fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) 22 | hsv_h: 0.015 # image HSV-Hue augmentation (fraction) 23 | hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) 24 | hsv_v: 0.4 # image HSV-Value augmentation (fraction) 25 | degrees: 0.0 # image rotation (+/- deg) 26 | translate: 0.1 # image translation (+/- fraction) 27 | scale: 0.9 # image scale (+/- gain) 28 | shear: 0.0 # image shear (+/- deg) 29 | perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 30 | flipud: 0.0 # image flip up-down (probability) 31 | fliplr: 0.5 # image flip left-right (probability) 32 | mosaic: 1.0 # image mosaic (probability) 33 | mixup: 0.1 # image mixup (probability) 34 | copy_paste: 0.0 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-low.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for low-augmentation COCO training from scratch 3 | # python train.py --batch 64 --cfg yolov5n6.yaml --weights '' --data coco.yaml --img 640 --epochs 300 --linear 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) 7 | lrf: 0.01 # final OneCycleLR learning rate (lr0 * lrf) 8 | momentum: 0.937 # SGD momentum/Adam beta1 9 | weight_decay: 0.0005 # optimizer weight decay 5e-4 10 | warmup_epochs: 3.0 # warmup epochs (fractions ok) 11 | warmup_momentum: 0.8 # warmup initial momentum 12 | warmup_bias_lr: 0.1 # warmup initial bias lr 13 | box: 0.05 # box loss gain 14 | cls: 0.5 # cls loss gain 15 | cls_pw: 1.0 # cls BCELoss positive_weight 16 | obj: 1.0 # obj loss gain (scale with pixels) 17 | obj_pw: 1.0 # obj BCELoss positive_weight 18 | iou_t: 0.20 # IoU training threshold 19 | anchor_t: 4.0 # anchor-multiple threshold 20 | # anchors: 3 # anchors per output layer (0 to ignore) 21 | fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) 22 | hsv_h: 0.015 # image HSV-Hue augmentation (fraction) 23 | hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) 24 | hsv_v: 0.4 # image HSV-Value augmentation (fraction) 25 | degrees: 0.0 # image rotation (+/- deg) 26 | translate: 0.1 # image translation (+/- fraction) 27 | scale: 0.5 # image scale (+/- gain) 28 | shear: 0.0 # image shear (+/- deg) 29 | perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 30 | flipud: 0.0 # image flip up-down (probability) 31 | fliplr: 0.5 # image flip left-right (probability) 32 | mosaic: 1.0 # image mosaic (probability) 33 | mixup: 0.0 # image mixup (probability) 34 | copy_paste: 0.0 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /models/hub/yolov5-p2.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 3 # AutoAnchor evolves 3 anchors per P output layer 8 | 9 | # YOLOv5 v6.0 backbone 10 | backbone: 11 | # [from, number, module, args] 12 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 13 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 14 | [-1, 3, C3, [128]], 15 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 16 | [-1, 6, C3, [256]], 17 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 18 | [-1, 9, C3, [512]], 19 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 20 | [-1, 3, C3, [1024]], 21 | [-1, 1, SPPF, [1024, 5]], # 9 22 | ] 23 | 24 | # YOLOv5 v6.0 head with (P2, P3, P4, P5) outputs 25 | head: 26 | [[-1, 1, Conv, [512, 1, 1]], 27 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 28 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 29 | [-1, 3, C3, [512, False]], # 13 30 | 31 | [-1, 1, Conv, [256, 1, 1]], 32 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 33 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 34 | [-1, 3, C3, [256, False]], # 17 (P3/8-small) 35 | 36 | [-1, 1, Conv, [128, 1, 1]], 37 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 38 | [[-1, 2], 1, Concat, [1]], # cat backbone P2 39 | [-1, 1, C3, [128, False]], # 21 (P2/4-xsmall) 40 | 41 | [-1, 1, Conv, [128, 3, 2]], 42 | [[-1, 18], 1, Concat, [1]], # cat head P3 43 | [-1, 3, C3, [256, False]], # 24 (P3/8-small) 44 | 45 | [-1, 1, Conv, [256, 3, 2]], 46 | [[-1, 14], 1, Concat, [1]], # cat head P4 47 | [-1, 3, C3, [512, False]], # 27 (P4/16-medium) 48 | 49 | [-1, 1, Conv, [512, 3, 2]], 50 | [[-1, 10], 1, Concat, [1]], # cat head P5 51 | [-1, 3, C3, [1024, False]], # 30 (P5/32-large) 52 | 53 | [[21, 24, 27, 30], 1, Detect, [nc, anchors]], # Detect(P2, P3, P4, P5) 54 | ] 55 | -------------------------------------------------------------------------------- /models/hub/yolov5-p6.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 3 # AutoAnchor evolves 3 anchors per P output layer 8 | 9 | # YOLOv5 v6.0 backbone 10 | backbone: 11 | # [from, number, module, args] 12 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 13 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 14 | [-1, 3, C3, [128]], 15 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 16 | [-1, 6, C3, [256]], 17 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 18 | [-1, 9, C3, [512]], 19 | [-1, 1, Conv, [768, 3, 2]], # 7-P5/32 20 | [-1, 3, C3, [768]], 21 | [-1, 1, Conv, [1024, 3, 2]], # 9-P6/64 22 | [-1, 3, C3, [1024]], 23 | [-1, 1, SPPF, [1024, 5]], # 11 24 | ] 25 | 26 | # YOLOv5 v6.0 head with (P3, P4, P5, P6) outputs 27 | head: 28 | [[-1, 1, Conv, [768, 1, 1]], 29 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 30 | [[-1, 8], 1, Concat, [1]], # cat backbone P5 31 | [-1, 3, C3, [768, False]], # 15 32 | 33 | [-1, 1, Conv, [512, 1, 1]], 34 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 35 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 36 | [-1, 3, C3, [512, False]], # 19 37 | 38 | [-1, 1, Conv, [256, 1, 1]], 39 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 40 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 41 | [-1, 3, C3, [256, False]], # 23 (P3/8-small) 42 | 43 | [-1, 1, Conv, [256, 3, 2]], 44 | [[-1, 20], 1, Concat, [1]], # cat head P4 45 | [-1, 3, C3, [512, False]], # 26 (P4/16-medium) 46 | 47 | [-1, 1, Conv, [512, 3, 2]], 48 | [[-1, 16], 1, Concat, [1]], # cat head P5 49 | [-1, 3, C3, [768, False]], # 29 (P5/32-large) 50 | 51 | [-1, 1, Conv, [768, 3, 2]], 52 | [[-1, 12], 1, Concat, [1]], # cat head P6 53 | [-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge) 54 | 55 | [[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6) 56 | ] 57 | -------------------------------------------------------------------------------- /models/hub/yolov5l6.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 8 | - [19,27, 44,40, 38,94] # P3/8 9 | - [96,68, 86,152, 180,137] # P4/16 10 | - [140,301, 303,264, 238,542] # P5/32 11 | - [436,615, 739,380, 925,792] # P6/64 12 | 13 | # YOLOv5 v6.0 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 17 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 18 | [-1, 3, C3, [128]], 19 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 20 | [-1, 6, C3, [256]], 21 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 22 | [-1, 9, C3, [512]], 23 | [-1, 1, Conv, [768, 3, 2]], # 7-P5/32 24 | [-1, 3, C3, [768]], 25 | [-1, 1, Conv, [1024, 3, 2]], # 9-P6/64 26 | [-1, 3, C3, [1024]], 27 | [-1, 1, SPPF, [1024, 5]], # 11 28 | ] 29 | 30 | # YOLOv5 v6.0 head 31 | head: 32 | [[-1, 1, Conv, [768, 1, 1]], 33 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 34 | [[-1, 8], 1, Concat, [1]], # cat backbone P5 35 | [-1, 3, C3, [768, False]], # 15 36 | 37 | [-1, 1, Conv, [512, 1, 1]], 38 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 39 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 40 | [-1, 3, C3, [512, False]], # 19 41 | 42 | [-1, 1, Conv, [256, 1, 1]], 43 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 44 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 45 | [-1, 3, C3, [256, False]], # 23 (P3/8-small) 46 | 47 | [-1, 1, Conv, [256, 3, 2]], 48 | [[-1, 20], 1, Concat, [1]], # cat head P4 49 | [-1, 3, C3, [512, False]], # 26 (P4/16-medium) 50 | 51 | [-1, 1, Conv, [512, 3, 2]], 52 | [[-1, 16], 1, Concat, [1]], # cat head P5 53 | [-1, 3, C3, [768, False]], # 29 (P5/32-large) 54 | 55 | [-1, 1, Conv, [768, 3, 2]], 56 | [[-1, 12], 1, Concat, [1]], # cat head P6 57 | [-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge) 58 | 59 | [[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /models/hub/yolov5m6.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 0.67 # model depth multiple 6 | width_multiple: 0.75 # layer channel multiple 7 | anchors: 8 | - [19,27, 44,40, 38,94] # P3/8 9 | - [96,68, 86,152, 180,137] # P4/16 10 | - [140,301, 303,264, 238,542] # P5/32 11 | - [436,615, 739,380, 925,792] # P6/64 12 | 13 | # YOLOv5 v6.0 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 17 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 18 | [-1, 3, C3, [128]], 19 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 20 | [-1, 6, C3, [256]], 21 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 22 | [-1, 9, C3, [512]], 23 | [-1, 1, Conv, [768, 3, 2]], # 7-P5/32 24 | [-1, 3, C3, [768]], 25 | [-1, 1, Conv, [1024, 3, 2]], # 9-P6/64 26 | [-1, 3, C3, [1024]], 27 | [-1, 1, SPPF, [1024, 5]], # 11 28 | ] 29 | 30 | # YOLOv5 v6.0 head 31 | head: 32 | [[-1, 1, Conv, [768, 1, 1]], 33 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 34 | [[-1, 8], 1, Concat, [1]], # cat backbone P5 35 | [-1, 3, C3, [768, False]], # 15 36 | 37 | [-1, 1, Conv, [512, 1, 1]], 38 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 39 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 40 | [-1, 3, C3, [512, False]], # 19 41 | 42 | [-1, 1, Conv, [256, 1, 1]], 43 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 44 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 45 | [-1, 3, C3, [256, False]], # 23 (P3/8-small) 46 | 47 | [-1, 1, Conv, [256, 3, 2]], 48 | [[-1, 20], 1, Concat, [1]], # cat head P4 49 | [-1, 3, C3, [512, False]], # 26 (P4/16-medium) 50 | 51 | [-1, 1, Conv, [512, 3, 2]], 52 | [[-1, 16], 1, Concat, [1]], # cat head P5 53 | [-1, 3, C3, [768, False]], # 29 (P5/32-large) 54 | 55 | [-1, 1, Conv, [768, 3, 2]], 56 | [[-1, 12], 1, Concat, [1]], # cat head P6 57 | [-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge) 58 | 59 | [[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /models/hub/yolov5n6.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 0.33 # model depth multiple 6 | width_multiple: 0.25 # layer channel multiple 7 | anchors: 8 | - [19,27, 44,40, 38,94] # P3/8 9 | - [96,68, 86,152, 180,137] # P4/16 10 | - [140,301, 303,264, 238,542] # P5/32 11 | - [436,615, 739,380, 925,792] # P6/64 12 | 13 | # YOLOv5 v6.0 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 17 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 18 | [-1, 3, C3, [128]], 19 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 20 | [-1, 6, C3, [256]], 21 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 22 | [-1, 9, C3, [512]], 23 | [-1, 1, Conv, [768, 3, 2]], # 7-P5/32 24 | [-1, 3, C3, [768]], 25 | [-1, 1, Conv, [1024, 3, 2]], # 9-P6/64 26 | [-1, 3, C3, [1024]], 27 | [-1, 1, SPPF, [1024, 5]], # 11 28 | ] 29 | 30 | # YOLOv5 v6.0 head 31 | head: 32 | [[-1, 1, Conv, [768, 1, 1]], 33 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 34 | [[-1, 8], 1, Concat, [1]], # cat backbone P5 35 | [-1, 3, C3, [768, False]], # 15 36 | 37 | [-1, 1, Conv, [512, 1, 1]], 38 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 39 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 40 | [-1, 3, C3, [512, False]], # 19 41 | 42 | [-1, 1, Conv, [256, 1, 1]], 43 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 44 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 45 | [-1, 3, C3, [256, False]], # 23 (P3/8-small) 46 | 47 | [-1, 1, Conv, [256, 3, 2]], 48 | [[-1, 20], 1, Concat, [1]], # cat head P4 49 | [-1, 3, C3, [512, False]], # 26 (P4/16-medium) 50 | 51 | [-1, 1, Conv, [512, 3, 2]], 52 | [[-1, 16], 1, Concat, [1]], # cat head P5 53 | [-1, 3, C3, [768, False]], # 29 (P5/32-large) 54 | 55 | [-1, 1, Conv, [768, 3, 2]], 56 | [[-1, 12], 1, Concat, [1]], # cat head P6 57 | [-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge) 58 | 59 | [[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /models/hub/yolov5s6.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 0.33 # model depth multiple 6 | width_multiple: 0.50 # layer channel multiple 7 | anchors: 8 | - [19,27, 44,40, 38,94] # P3/8 9 | - [96,68, 86,152, 180,137] # P4/16 10 | - [140,301, 303,264, 238,542] # P5/32 11 | - [436,615, 739,380, 925,792] # P6/64 12 | 13 | # YOLOv5 v6.0 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 17 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 18 | [-1, 3, C3, [128]], 19 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 20 | [-1, 6, C3, [256]], 21 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 22 | [-1, 9, C3, [512]], 23 | [-1, 1, Conv, [768, 3, 2]], # 7-P5/32 24 | [-1, 3, C3, [768]], 25 | [-1, 1, Conv, [1024, 3, 2]], # 9-P6/64 26 | [-1, 3, C3, [1024]], 27 | [-1, 1, SPPF, [1024, 5]], # 11 28 | ] 29 | 30 | # YOLOv5 v6.0 head 31 | head: 32 | [[-1, 1, Conv, [768, 1, 1]], 33 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 34 | [[-1, 8], 1, Concat, [1]], # cat backbone P5 35 | [-1, 3, C3, [768, False]], # 15 36 | 37 | [-1, 1, Conv, [512, 1, 1]], 38 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 39 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 40 | [-1, 3, C3, [512, False]], # 19 41 | 42 | [-1, 1, Conv, [256, 1, 1]], 43 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 44 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 45 | [-1, 3, C3, [256, False]], # 23 (P3/8-small) 46 | 47 | [-1, 1, Conv, [256, 3, 2]], 48 | [[-1, 20], 1, Concat, [1]], # cat head P4 49 | [-1, 3, C3, [512, False]], # 26 (P4/16-medium) 50 | 51 | [-1, 1, Conv, [512, 3, 2]], 52 | [[-1, 16], 1, Concat, [1]], # cat head P5 53 | [-1, 3, C3, [768, False]], # 29 (P5/32-large) 54 | 55 | [-1, 1, Conv, [768, 3, 2]], 56 | [[-1, 12], 1, Concat, [1]], # cat head P6 57 | [-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge) 58 | 59 | [[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /models/hub/yolov5x6.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.33 # model depth multiple 6 | width_multiple: 1.25 # layer channel multiple 7 | anchors: 8 | - [19,27, 44,40, 38,94] # P3/8 9 | - [96,68, 86,152, 180,137] # P4/16 10 | - [140,301, 303,264, 238,542] # P5/32 11 | - [436,615, 739,380, 925,792] # P6/64 12 | 13 | # YOLOv5 v6.0 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 17 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 18 | [-1, 3, C3, [128]], 19 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 20 | [-1, 6, C3, [256]], 21 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 22 | [-1, 9, C3, [512]], 23 | [-1, 1, Conv, [768, 3, 2]], # 7-P5/32 24 | [-1, 3, C3, [768]], 25 | [-1, 1, Conv, [1024, 3, 2]], # 9-P6/64 26 | [-1, 3, C3, [1024]], 27 | [-1, 1, SPPF, [1024, 5]], # 11 28 | ] 29 | 30 | # YOLOv5 v6.0 head 31 | head: 32 | [[-1, 1, Conv, [768, 1, 1]], 33 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 34 | [[-1, 8], 1, Concat, [1]], # cat backbone P5 35 | [-1, 3, C3, [768, False]], # 15 36 | 37 | [-1, 1, Conv, [512, 1, 1]], 38 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 39 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 40 | [-1, 3, C3, [512, False]], # 19 41 | 42 | [-1, 1, Conv, [256, 1, 1]], 43 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 44 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 45 | [-1, 3, C3, [256, False]], # 23 (P3/8-small) 46 | 47 | [-1, 1, Conv, [256, 3, 2]], 48 | [[-1, 20], 1, Concat, [1]], # cat head P4 49 | [-1, 3, C3, [512, False]], # 26 (P4/16-medium) 50 | 51 | [-1, 1, Conv, [512, 3, 2]], 52 | [[-1, 16], 1, Concat, [1]], # cat head P5 53 | [-1, 3, C3, [768, False]], # 29 (P5/32-large) 54 | 55 | [-1, 1, Conv, [768, 3, 2]], 56 | [[-1, 12], 1, Concat, [1]], # cat head P6 57 | [-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge) 58 | 59 | [[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /tools/process_labels/noise_missing.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | 5 | def missing_ratio(rate): 6 | a = random.randint(0, 99) 7 | noise = False 8 | if a < rate: 9 | noise = True 10 | return noise 11 | 12 | 13 | def noise_txt(txt_file, target, rate): 14 | target_file = txt_file.replace('labels_ori_txt', target) 15 | 16 | with open(target_file, 'w+') as saver: 17 | with open(txt_file, 'r+') as file: 18 | lines = file.readlines() 19 | file.seek(0, 0) #set the pointer to 0,0 cordinate of file 20 | for line in lines: 21 | missing_label = missing_ratio(rate) 22 | if missing_label is not True: 23 | saver.write(line) 24 | saver.close() 25 | 26 | 27 | def run(txt_path='/Volumes/Data/nosie_labels/', target = None, Rate=5): 28 | for roots, dirs, files in os.walk(txt_path): 29 | target_dir = roots.replace('labels_ori_txt', target) 30 | if not os.path.exists(target_dir): 31 | os.mkdir(target_dir) 32 | for file in files: 33 | if file.endswith('.txt'): 34 | file_path = os.path.join(roots, file) 35 | noise_txt(txt_file=file_path, target = target, rate=Rate) 36 | print("Noise rate {} Done!".format(Rate)) 37 | 38 | 39 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise5',Rate=5) 40 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise10',Rate=10) 41 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise15',Rate=15) 42 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise20',Rate=20) 43 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise25',Rate=25) 44 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise30',Rate=30) 45 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise35',Rate=35) 46 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise40',Rate=40) 47 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise45',Rate=45) 48 | run(txt_path='/Users/Dong/Desktop/paprika_v4/labels_ori_txt', target='labels_missing_noise50',Rate=50) -------------------------------------------------------------------------------- /models/hub/yolov5-p7.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 7 | anchors: 3 # AutoAnchor evolves 3 anchors per P output layer 8 | 9 | # YOLOv5 v6.0 backbone 10 | backbone: 11 | # [from, number, module, args] 12 | [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 13 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 14 | [-1, 3, C3, [128]], 15 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 16 | [-1, 6, C3, [256]], 17 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 18 | [-1, 9, C3, [512]], 19 | [-1, 1, Conv, [768, 3, 2]], # 7-P5/32 20 | [-1, 3, C3, [768]], 21 | [-1, 1, Conv, [1024, 3, 2]], # 9-P6/64 22 | [-1, 3, C3, [1024]], 23 | [-1, 1, Conv, [1280, 3, 2]], # 11-P7/128 24 | [-1, 3, C3, [1280]], 25 | [-1, 1, SPPF, [1280, 5]], # 13 26 | ] 27 | 28 | # YOLOv5 v6.0 head with (P3, P4, P5, P6, P7) outputs 29 | head: 30 | [[-1, 1, Conv, [1024, 1, 1]], 31 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 32 | [[-1, 10], 1, Concat, [1]], # cat backbone P6 33 | [-1, 3, C3, [1024, False]], # 17 34 | 35 | [-1, 1, Conv, [768, 1, 1]], 36 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 37 | [[-1, 8], 1, Concat, [1]], # cat backbone P5 38 | [-1, 3, C3, [768, False]], # 21 39 | 40 | [-1, 1, Conv, [512, 1, 1]], 41 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 42 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 43 | [-1, 3, C3, [512, False]], # 25 44 | 45 | [-1, 1, Conv, [256, 1, 1]], 46 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 47 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 48 | [-1, 3, C3, [256, False]], # 29 (P3/8-small) 49 | 50 | [-1, 1, Conv, [256, 3, 2]], 51 | [[-1, 26], 1, Concat, [1]], # cat head P4 52 | [-1, 3, C3, [512, False]], # 32 (P4/16-medium) 53 | 54 | [-1, 1, Conv, [512, 3, 2]], 55 | [[-1, 22], 1, Concat, [1]], # cat head P5 56 | [-1, 3, C3, [768, False]], # 35 (P5/32-large) 57 | 58 | [-1, 1, Conv, [768, 3, 2]], 59 | [[-1, 18], 1, Concat, [1]], # cat head P6 60 | [-1, 3, C3, [1024, False]], # 38 (P6/64-xlarge) 61 | 62 | [-1, 1, Conv, [1024, 3, 2]], 63 | [[-1, 14], 1, Concat, [1]], # cat head P7 64 | [-1, 3, C3, [1280, False]], # 41 (P7/128-xxlarge) 65 | 66 | [[29, 32, 35, 38, 41], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6, P7) 67 | ] 68 | -------------------------------------------------------------------------------- /utils/autobatch.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Auto-batch utils 4 | """ 5 | 6 | from copy import deepcopy 7 | 8 | import numpy as np 9 | import torch 10 | from torch.cuda import amp 11 | 12 | from utils.general import LOGGER, colorstr 13 | from utils.torch_utils import profile 14 | 15 | 16 | def check_train_batch_size(model, imgsz=640): 17 | # Check YOLOv5 training batch size 18 | with amp.autocast(): 19 | return autobatch(deepcopy(model).train(), imgsz) # compute optimal batch size 20 | 21 | 22 | def autobatch(model, imgsz=640, fraction=0.9, batch_size=16): 23 | # Automatically estimate best batch size to use `fraction` of available CUDA memory 24 | # Usage: 25 | # import torch 26 | # from utils.autobatch import autobatch 27 | # model = torch.hub.load('ultralytics/yolov5', 'yolov5s', autoshape=False) 28 | # print(autobatch(model)) 29 | 30 | prefix = colorstr('AutoBatch: ') 31 | LOGGER.info(f'{prefix}Computing optimal batch size for --imgsz {imgsz}') 32 | device = next(model.parameters()).device # get model device 33 | if device.type == 'cpu': 34 | LOGGER.info(f'{prefix}CUDA not detected, using default CPU batch-size {batch_size}') 35 | return batch_size 36 | 37 | d = str(device).upper() # 'CUDA:0' 38 | properties = torch.cuda.get_device_properties(device) # device properties 39 | t = properties.total_memory / 1024 ** 3 # (GiB) 40 | r = torch.cuda.memory_reserved(device) / 1024 ** 3 # (GiB) 41 | a = torch.cuda.memory_allocated(device) / 1024 ** 3 # (GiB) 42 | f = t - (r + a) # free inside reserved 43 | LOGGER.info(f'{prefix}{d} ({properties.name}) {t:.2f}G total, {r:.2f}G reserved, {a:.2f}G allocated, {f:.2f}G free') 44 | 45 | batch_sizes = [1, 2, 4, 8, 16] 46 | try: 47 | img = [torch.zeros(b, 3, imgsz, imgsz) for b in batch_sizes] 48 | y = profile(img, model, n=3, device=device) 49 | except Exception as e: 50 | LOGGER.warning(f'{prefix}{e}') 51 | 52 | y = [x[2] for x in y if x] # memory [2] 53 | batch_sizes = batch_sizes[:len(y)] 54 | p = np.polyfit(batch_sizes, y, deg=1) # first degree polynomial fit 55 | b = int((f * fraction - p[1]) / p[0]) # y intercept (optimal batch size) 56 | LOGGER.info(f'{prefix}Using batch-size {b} for {d} {t * fraction:.2f}G/{t:.2f}G ({fraction * 100:.0f}%)') 57 | return b 58 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Start FROM Nvidia PyTorch image https://ngc.nvidia.com/catalog/containers/nvidia:pytorch 4 | FROM nvcr.io/nvidia/pytorch:21.10-py3 5 | 6 | # Install linux packages 7 | RUN apt update && apt install -y zip htop screen libgl1-mesa-glx 8 | 9 | # Install python dependencies 10 | COPY requirements.txt . 11 | RUN python -m pip install --upgrade pip 12 | RUN pip uninstall -y nvidia-tensorboard nvidia-tensorboard-plugin-dlprof 13 | RUN pip install --no-cache -r requirements.txt albumentations coremltools onnx gsutil notebook numpy Pillow wandb>=0.12.2 14 | RUN pip install --no-cache torch==1.10.1+cu113 torchvision==0.11.2+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html 15 | # RUN pip install --no-cache -U torch torchvision 16 | 17 | # Create working directory 18 | RUN mkdir -p /usr/src/app 19 | WORKDIR /usr/src/app 20 | 21 | # Copy contents 22 | COPY . /usr/src/app 23 | 24 | # Downloads to user config dir 25 | ADD https://ultralytics.com/assets/Arial.ttf /root/.config/Ultralytics/ 26 | 27 | # Set environment variables 28 | # ENV HOME=/usr/src/app 29 | 30 | 31 | # Usage Examples ------------------------------------------------------------------------------------------------------- 32 | 33 | # Build and Push 34 | # t=ultralytics/yolov5:latest && sudo docker build -t $t . && sudo docker push $t 35 | 36 | # Pull and Run 37 | # t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all $t 38 | 39 | # Pull and Run with local directory access 40 | # t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all -v "$(pwd)"/datasets:/usr/src/datasets $t 41 | 42 | # Kill all 43 | # sudo docker kill $(sudo docker ps -q) 44 | 45 | # Kill all image-based 46 | # sudo docker kill $(sudo docker ps -qa --filter ancestor=ultralytics/yolov5:latest) 47 | 48 | # Bash into running container 49 | # sudo docker exec -it 5a9b5863d93d bash 50 | 51 | # Bash into stopped container 52 | # id=$(sudo docker ps -qa) && sudo docker start $id && sudo docker exec -it $id bash 53 | 54 | # Clean up 55 | # docker system prune -a --volumes 56 | 57 | # Update Ubuntu drivers 58 | # https://www.maketecheasier.com/install-nvidia-drivers-ubuntu/ 59 | 60 | # DDP test 61 | # python -m torch.distributed.run --nproc_per_node 2 --master_port 1 train.py --epochs 3 62 | 63 | # GCP VM from Image 64 | # docker.io/ultralytics/yolov5:latest 65 | -------------------------------------------------------------------------------- /tools/process_labels/noise_lable.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | 5 | def noise_ratio(rate): 6 | a = random.randint(0, 99) 7 | noise = False 8 | if a < rate: 9 | noise = True 10 | return noise 11 | 12 | 13 | def noise_label(org_label): 14 | if org_label == '0': 15 | new_label = random.choice('1234') 16 | elif org_label == '1': 17 | new_label = random.choice('0234') 18 | elif org_label == '2': 19 | new_label = random.choice('0134') 20 | elif org_label == '3': 21 | new_label = random.choice('0124') 22 | else: 23 | new_label = random.choice('0123') 24 | return new_label 25 | 26 | 27 | def noise_txt(txt_file, rate): 28 | with open(txt_file, 'r+') as file: 29 | lines = file.readlines() 30 | file.seek(0, 0) #set the pointer to 0,0 cordinate of file 31 | for line in lines: 32 | row = line.strip().split(" ") 33 | noise = noise_ratio(rate) 34 | if noise: 35 | wrong_label = noise_label(row[0]) 36 | print("The label is changed from {} to {}.".format(row[0], wrong_label)) 37 | row[0] = wrong_label 38 | file.write(" ".join(row) + "\n") 39 | 40 | 41 | def run(txt_path='/Volumes/Data/nosie_labels/', Rate=5): 42 | 43 | for roots, dirs, files in os.walk(txt_path): 44 | print(roots) 45 | for file in files: 46 | if file.endswith('.txt'): 47 | file_path = os.path.join(roots, file) 48 | noise_txt(txt_file=file_path, rate=Rate) 49 | 50 | print("Noise rate {} Done!".format(Rate)) 51 | 52 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise5/', Rate=5) 53 | 54 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise10/', Rate=15) 55 | 56 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise15/', Rate=15) 57 | 58 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise20/.', Rate=20) 59 | 60 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise25/.', Rate=25) 61 | 62 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise30/.', Rate=30) 63 | 64 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise35/.', Rate=35) 65 | 66 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise40/.', Rate=40) 67 | 68 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise45/.', Rate=45) 69 | 70 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_noise50/.', Rate=50) 71 | 72 | 73 | -------------------------------------------------------------------------------- /tools/process_labels/remove_object_on_tomato.py: -------------------------------------------------------------------------------- 1 | # Alvaro's task. 2 | 3 | import xml.etree.ElementTree as ET 4 | from lxml import etree 5 | import os 6 | 7 | 8 | def get_value(category, xmin, xmax, ymin, ymax): 9 | category = str(category.text) 10 | xmin = int(xmin.text) 11 | xmax = int(xmax.text) 12 | ymin = int(ymin.text) 13 | ymax = int(ymax.text) 14 | 15 | return category, xmin, xmax, ymin, ymax 16 | 17 | 18 | def calculate_ratio(width, height, xmin, xmax, ymin, ymax): 19 | img = width*height 20 | instance = (xmax-xmin) * (ymax - ymin) 21 | ratio = instance/img 22 | 23 | return ratio 24 | 25 | 26 | def remove_small_instance(xml_file, target): 27 | SAVE =True 28 | target_file = xml_file.replace('labels', target) 29 | 30 | parser = etree.XMLParser(remove_blank_text=True) # 31 | root = etree.parse(xml_file, parser) 32 | tree = etree.ElementTree(root.getroot()) 33 | 34 | size = root.find('size') 35 | width = int(size.find('width').text) 36 | height = int(size.find('height').text) 37 | 38 | small = 0 39 | 40 | for object in tree.findall('object'): 41 | category = object.find('name') 42 | bndbox = object.find('bndbox') # 子节点下节点rank的值 43 | xmin = bndbox.find('xmin') # type(xmin) = int 44 | xmax = bndbox.find('xmax') 45 | ymin = bndbox.find('ymin') 46 | ymax = bndbox.find('ymax') 47 | 48 | old_category, old_xmin, old_xmax, old_ymin, old_ymax = get_value(category, xmin, xmax, ymin, ymax) 49 | ratio = calculate_ratio(width, height, old_xmin, old_xmax, old_ymin, old_ymax) 50 | if ratio<0.05: 51 | if old_category not in ['wfly', 'yculr', 'eggfly', 'healthy']: 52 | small+=1 53 | if small > 3: 54 | SAVE=False 55 | if old_category in ["wilt", "death", "spot"]: # remove instance 56 | parent = object.getparent() 57 | parent.remove(object) 58 | if SAVE: 59 | tree.write(target_file, pretty_print=True, xml_declaration=False, encoding='utf-8') 60 | else: 61 | image_path = xml_file.replace('labels', 'JPEGImages') 62 | image_path= image_path.replace('.xml', '.jpg') 63 | if os.path.exists(image_path): 64 | os.remove(image_path) 65 | 66 | 67 | def run(xml_path=None, target=None): 68 | for roots, dirs, files in os.walk(xml_path): 69 | print(roots) 70 | target_dir = roots.replace('labels', target) 71 | if not os.path.exists(target_dir): 72 | os.mkdir(target_dir) 73 | for file in files: 74 | if file.endswith('.xml'): 75 | file_path = os.path.join(roots, file) 76 | remove_small_instance(xml_file=file_path, target=target) 77 | 78 | print("Done!") 79 | 80 | 81 | run(xml_path='/Volumes/Data/project_paper_code_data/Plant_diseases_dataset/tomoto_v3/labels/', target='labels_big') 82 | 83 | -------------------------------------------------------------------------------- /tools/process_labels/remove_object_on_paprika.py: -------------------------------------------------------------------------------- 1 | # Alvaro's task. 2 | 3 | import xml.etree.ElementTree as ET 4 | from lxml import etree 5 | import os 6 | 7 | 8 | def get_value(category, xmin, xmax, ymin, ymax): 9 | category = str(category.text) 10 | xmin = int(xmin.text) 11 | xmax = int(xmax.text) 12 | ymin = int(ymin.text) 13 | ymax = int(ymax.text) 14 | 15 | return category, xmin, xmax, ymin, ymax 16 | 17 | 18 | def calculate_ratio(width, height, xmin, xmax, ymin, ymax): 19 | img = width*height 20 | instance = (xmax-xmin) * (ymax - ymin) 21 | ratio = instance/img 22 | 23 | return ratio 24 | 25 | 26 | def remove_small_instance(xml_file, target): 27 | SAVE =True 28 | target_file = xml_file.replace('labels', target) 29 | 30 | parser = etree.XMLParser(remove_blank_text=True) # 31 | root = etree.parse(xml_file, parser) 32 | tree = etree.ElementTree(root.getroot()) 33 | 34 | size = root.find('size') 35 | width = int(size.find('width').text) 36 | height = int(size.find('height').text) 37 | 38 | small = 0 39 | 40 | for object in tree.findall('object'): 41 | category = object.find('name') 42 | bndbox = object.find('bndbox') # 子节点下节点rank的值 43 | xmin = bndbox.find('xmin') # type(xmin) = int 44 | xmax = bndbox.find('xmax') 45 | ymin = bndbox.find('ymin') 46 | ymax = bndbox.find('ymax') 47 | 48 | old_category, old_xmin, old_xmax, old_ymin, old_ymax = get_value(category, xmin, xmax, ymin, ymax) 49 | ratio = calculate_ratio(width, height, old_xmin, old_xmax, old_ymin, old_ymax) 50 | if ratio<0.05: 51 | if old_category not in ['wfly', 'yculr', 'eggfly', 'healthy']: 52 | small+=1 53 | if small > 3: 54 | SAVE=False 55 | if old_category in ["wilt", "death", "spot"]: # remove instance 56 | parent = object.getparent() 57 | parent.remove(object) 58 | if SAVE: 59 | tree.write(target_file, pretty_print=True, xml_declaration=False, encoding='utf-8') 60 | else: 61 | image_path = xml_file.replace('labels', 'show_xml') 62 | image_path= image_path.replace('.xml', '.jpg') 63 | if os.path.exists(image_path): 64 | os.remove(image_path) 65 | 66 | 67 | def run(xml_path=None, target=None): 68 | for roots, dirs, files in os.walk(xml_path): 69 | print(roots) 70 | target_dir = roots.replace('labels', target) 71 | if not os.path.exists(target_dir): 72 | os.mkdir(target_dir) 73 | for file in files: 74 | if file.endswith('.xml'): 75 | file_path = os.path.join(roots, file) 76 | remove_small_instance(xml_file=file_path, target=target) 77 | 78 | print("Done!") 79 | 80 | 81 | run(xml_path='/Volumes/Data/project_paper_code_data/Plant_diseases_dataset/tomoto_v3/labels/', 82 | target='labels_big') 83 | 84 | -------------------------------------------------------------------------------- /tools/process_images/modify_orientation_mpo.py: -------------------------------------------------------------------------------- 1 | import PIL.Image 2 | import PIL.ImageOps 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import os 6 | import cv2 7 | 8 | # image_dir = '/Users/jiuqingdong/Desktop/test/JPEGImages/' 9 | image_dir = '/Volumes/Data/project_paper_code_data/Plant_diseases_dataset/Tomato_dataset/JPEGImages/' 10 | output_dir = '/Volumes/Data/project_paper_code_data/Plant_diseases_dataset/Tomato_dataset/JPEGImages_modify_orientation/' 11 | 12 | 13 | def exif_transpose_hand(img, orientation): 14 | #if not img: 15 | # return img 16 | if orientation == None: 17 | return img 18 | else: 19 | image = img.rotate(0, expand=True) 20 | return image 21 | 22 | 23 | def exif_transpose(img): 24 | if not img: 25 | return img 26 | 27 | exif_orientation_tag = 274 28 | image = img 29 | # Check for EXIF data (only present on some files) 30 | if hasattr(img, "_getexif") and isinstance(img._getexif(), dict) and exif_orientation_tag in img._getexif(): 31 | exif_data = img._getexif() 32 | orientation = exif_data[exif_orientation_tag] 33 | print("ori",orientation) 34 | # Handle EXIF Orientation 35 | if orientation == 1: 36 | # Normal image - nothing to do! 37 | pass 38 | elif orientation == 2: 39 | # Mirrored left to right 40 | image = img.transpose(PIL.Image.FLIP_LEFT_RIGHT) 41 | elif orientation == 3: 42 | # Rotated 180 degrees 43 | image = img.rotate(180) 44 | elif orientation == 4: 45 | # Mirrored top to bottom 46 | image = img.rotate(180).transpose(PIL.Image.FLIP_LEFT_RIGHT) 47 | elif orientation == 5: 48 | # Mirrored along top-left diagonal 49 | image = img.rotate(-90, expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT) 50 | elif orientation == 6: 51 | # Rotated 90 degrees 52 | image = img.rotate(-90, expand=True) 53 | elif orientation == 7: 54 | # Mirrored along top-right diagonal 55 | image = img.rotate(90, expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT) 56 | elif orientation == 8: 57 | # Rotated 270 degrees 58 | image = img.rotate(90, expand=True) 59 | elif orientation == None: 60 | image = img.rotate(0, expand=True) 61 | return image 62 | 63 | 64 | def load_image_file(file, mode='RGB'): 65 | # Load the image with PIL 66 | img = PIL.Image.open(file) 67 | if hasattr(PIL.ImageOps, 'exif_transpose'): 68 | a = img.getexif().get(0x0112) 69 | print(file, ' = = = = = ', a) 70 | image = exif_transpose_hand(img, a) 71 | else: 72 | print("=else=") 73 | 74 | image = image.convert(mode) 75 | #return img 76 | return np.array(image) 77 | 78 | for root, dirs, files in os.walk(image_dir): 79 | for file in files: 80 | filename = os.path.join(root, file) 81 | img = load_image_file(filename) 82 | plt.imsave(output_dir+file, img) 83 | -------------------------------------------------------------------------------- /tools/process_images/modify_orientation_jpeg.py: -------------------------------------------------------------------------------- 1 | import PIL.Image 2 | import PIL.ImageOps 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import os 6 | import cv2 7 | 8 | def exif_transpose(img): 9 | if not img: 10 | return img 11 | 12 | exif_orientation_tag = 274 13 | 14 | # Check for EXIF data (only present on some files) 15 | if hasattr(img, "_getexif") and isinstance(img._getexif(), dict) and exif_orientation_tag in img._getexif(): 16 | exif_data = img._getexif() 17 | orientation = exif_data[exif_orientation_tag] 18 | 19 | # Handle EXIF Orientation 20 | if orientation == 1: 21 | # Normal image - nothing to do! 22 | pass 23 | elif orientation == 2: 24 | # Mirrored left to right 25 | img = img.transpose(PIL.Image.FLIP_LEFT_RIGHT) 26 | elif orientation == 3: 27 | # Rotated 180 degrees 28 | img = img.rotate(180) 29 | elif orientation == 4: 30 | # Mirrored top to bottom 31 | img = img.rotate(180).transpose(PIL.Image.FLIP_LEFT_RIGHT) 32 | elif orientation == 5: 33 | # Mirrored along top-left diagonal 34 | img = img.rotate(-90, expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT) 35 | elif orientation == 6: 36 | # Rotated 90 degrees 37 | img = img.rotate(-90, expand=True) 38 | elif orientation == 7: 39 | # Mirrored along top-right diagonal 40 | img = img.rotate(90, expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT) 41 | elif orientation == 8: 42 | # Rotated 270 degrees 43 | img = img.rotate(90, expand=True) 44 | 45 | return img 46 | 47 | 48 | def load_image_file(file, mode='RGB'): 49 | # Load the image with PIL 50 | img = PIL.Image.open(file) 51 | if hasattr(PIL.ImageOps, 'exif_transpose'): 52 | # Very recent versions of PIL can do exit transpose internally 53 | # print(" if hasattr(PIL.ImageOps, 'exif_transpose'):") 54 | a = img.getexif().get(0x0112) 55 | # if a == 1 or a == None: 56 | print(file, ' = = = = = ', a) 57 | img = PIL.ImageOps.exif_transpose(img) 58 | 59 | # img = exif_transpose(img) 60 | else: 61 | print("=else=") 62 | # Otherwise, do the exif transpose ourselves 63 | img = exif_transpose(img) 64 | 65 | img = img.convert(mode) 66 | #return img 67 | return np.array(img) 68 | 69 | # image_dir = '/Users/Dong/Desktop/project_paper_code_data/Plant_diseases_dataset/Paprika Dataset_v_1.02/images' 70 | image_dir = '/Users/jiuqingdong/Desktop/test/JPEGImages/' 71 | output_dir = '/Users/jiuqingdong/Desktop/test/train/' 72 | 73 | # for set in ['train/', 'val/', 'test/']: 74 | # image_dir = image_dir0+set 75 | # output_dir = output_dir0+set 76 | 77 | for root, dirs, files in os.walk(image_dir): 78 | for file in files: 79 | filename = os.path.join(root, file) 80 | img = load_image_file(filename) 81 | 82 | plt.imshow(img) 83 | plt.imsave(output_dir+file, img) 84 | plt.pause(1) 85 | plt.close() 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note 2 | This repository includes the official implementation of the paper: 3 | 4 | Data-centric Annotation Analysis for Plant Disease Detection: Strategy, Consistency, and Performance. 5 | 6 | 7 | Authors and affiliations: 8 | 9 | Jiuqing Dong 1, Jaehwan Lee1, 2, Alvaro Fuentes 1,2, Sook Yoon 3,, Mun Haeng Lee 4, Dong Sun Park 1,2, 10 | 1 Department of Electronic Engineering, Jeonbuk National University, Jeonju, South Korea 11 | 2 Core Research Institute of Intelligent Robots, Jeonbuk National University, Jeonju, South Korea 12 | 3 Department of Computer Engineering, Mokpo National University, Muan, South Korea 13 | 4 Fruit Vegetable Research Institute, Chungnam A.R.E.S, Buyeo, South Korea 14 | 15 | The code include YOLO-v5 implement, Noise generation, Data-augmentation by rotation, Visualization module, and Label processing part. In addition, we will provide the pretrained model of our paprika dataset. However, due to non-disclosure agreements, we are temporarily unable to make the dataset public. 16 | 17 | You also can refer to the Official implement of YOLO-v5. https://github.com/ultralytics/yolov5 18 | 19 | You need to modify the path to be able to run on your dataset. 20 | 21 | # Install 22 | 23 | Clone repo and install requirements.txt in a Python>=3.7.0 environment, including PyTorch>=1.7. 24 | 25 | cd yolov5 26 | 27 | pip install -r requirements.txt # install 28 | 29 | # Training 30 | 31 | python train.py --img 640 --batch 16 --epochs 200 --data data/paprika_v4.yaml --cfg models/yolov5x.yaml --weights weights/yolov5x.pt --device 1 --project paprika_v4/x 32 | 33 | --data: the configuration of training. 34 | 35 | -- weights: the pre-trained model 36 | 37 | --divice: GPU index 38 | 39 | --project: a folder for saving output 40 | 41 | 42 | # Testing 43 | python test.py --data data/paprika_v4.yaml --weights paprika_v4/x/exp/weights/best.pt --device 2 --project paprika_v4/x 44 | 45 | # Inference 46 | python detect.py --source /home/multiai3/Jiuqing/yolo5-official-new/datasets/paprika_v4/images/test --weights paprika_v4/x/exp/weights/best.pt --device 2 --project paprika_v4/x 47 | 48 | --source: source of image. 49 | 50 | # Visualization 51 | python detect_visualization.py --source /home/multiai3/Jiuqing/yolo5-official-new/datasets/paprika_v4/images/test --weights paprika_v4/x/exp/weights/best.pt --device 2 --project paprika_v4/x 52 | 53 | --source: source of image. 54 | 55 | # Data augmentation 56 | Please refer to ./tools/process_images/data_aug_*.py 57 | 58 | # Split the datasets 59 | Please refer to ./tools/process_images/split_the_datasets.py 60 | 61 | # Modify orientatiion 62 | Please refer to https://medium.com/@ageitgey/the-dumb-reason-your-fancy-computer-vision-app-isnt-working-exif-orientation-73166c7d39da 63 | 64 | If you don't have this problem, please ignore this. 65 | 66 | Otherwise, please refer to ./tools/process_images/modify_orientation.py 67 | 68 | # Noise generation: 69 | Please refer to ./tools/process_labels/noise*.py 70 | 71 | # Calculate the instance 72 | Please refer to ./tools/process_labels/calculate_bounding_box.py 73 | 74 | # Show your annotations 75 | Please refer to ./tools/show_xml/*.py 76 | 77 | # Pre-trained model 78 | The pretrained model will be released as soon as possible. 79 | -------------------------------------------------------------------------------- /utils/loggers/wandb/sweep.yaml: -------------------------------------------------------------------------------- 1 | # Hyperparameters for training 2 | # To set range- 3 | # Provide min and max values as: 4 | # parameter: 5 | # 6 | # min: scalar 7 | # max: scalar 8 | # OR 9 | # 10 | # Set a specific list of search space- 11 | # parameter: 12 | # values: [scalar1, scalar2, scalar3...] 13 | # 14 | # You can use grid, bayesian and hyperopt search strategy 15 | # For more info on configuring sweeps visit - https://docs.wandb.ai/guides/sweeps/configuration 16 | 17 | program: utils/loggers/wandb/sweep.py 18 | method: random 19 | metric: 20 | name: metrics/mAP_0.5 21 | goal: maximize 22 | 23 | parameters: 24 | # hyperparameters: set either min, max range or values list 25 | data: 26 | value: "data/coco128.yaml" 27 | batch_size: 28 | values: [64] 29 | epochs: 30 | values: [10] 31 | 32 | lr0: 33 | distribution: uniform 34 | min: 1e-5 35 | max: 1e-1 36 | lrf: 37 | distribution: uniform 38 | min: 0.01 39 | max: 1.0 40 | momentum: 41 | distribution: uniform 42 | min: 0.6 43 | max: 0.98 44 | weight_decay: 45 | distribution: uniform 46 | min: 0.0 47 | max: 0.001 48 | warmup_epochs: 49 | distribution: uniform 50 | min: 0.0 51 | max: 5.0 52 | warmup_momentum: 53 | distribution: uniform 54 | min: 0.0 55 | max: 0.95 56 | warmup_bias_lr: 57 | distribution: uniform 58 | min: 0.0 59 | max: 0.2 60 | box: 61 | distribution: uniform 62 | min: 0.02 63 | max: 0.2 64 | cls: 65 | distribution: uniform 66 | min: 0.2 67 | max: 4.0 68 | cls_pw: 69 | distribution: uniform 70 | min: 0.5 71 | max: 2.0 72 | obj: 73 | distribution: uniform 74 | min: 0.2 75 | max: 4.0 76 | obj_pw: 77 | distribution: uniform 78 | min: 0.5 79 | max: 2.0 80 | iou_t: 81 | distribution: uniform 82 | min: 0.1 83 | max: 0.7 84 | anchor_t: 85 | distribution: uniform 86 | min: 2.0 87 | max: 8.0 88 | fl_gamma: 89 | distribution: uniform 90 | min: 0.0 91 | max: 0.1 92 | hsv_h: 93 | distribution: uniform 94 | min: 0.0 95 | max: 0.1 96 | hsv_s: 97 | distribution: uniform 98 | min: 0.0 99 | max: 0.9 100 | hsv_v: 101 | distribution: uniform 102 | min: 0.0 103 | max: 0.9 104 | degrees: 105 | distribution: uniform 106 | min: 0.0 107 | max: 45.0 108 | translate: 109 | distribution: uniform 110 | min: 0.0 111 | max: 0.9 112 | scale: 113 | distribution: uniform 114 | min: 0.0 115 | max: 0.9 116 | shear: 117 | distribution: uniform 118 | min: 0.0 119 | max: 10.0 120 | perspective: 121 | distribution: uniform 122 | min: 0.0 123 | max: 0.001 124 | flipud: 125 | distribution: uniform 126 | min: 0.0 127 | max: 1.0 128 | fliplr: 129 | distribution: uniform 130 | min: 0.0 131 | max: 1.0 132 | mosaic: 133 | distribution: uniform 134 | min: 0.0 135 | max: 1.0 136 | mixup: 137 | distribution: uniform 138 | min: 0.0 139 | max: 1.0 140 | copy_paste: 141 | distribution: uniform 142 | min: 0.0 143 | max: 1.0 144 | -------------------------------------------------------------------------------- /models/hub/anchors.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Default anchors for COCO data 3 | 4 | 5 | # P5 ------------------------------------------------------------------------------------------------------------------- 6 | # P5-640: 7 | anchors_p5_640: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | 13 | # P6 ------------------------------------------------------------------------------------------------------------------- 14 | # P6-640: thr=0.25: 0.9964 BPR, 5.54 anchors past thr, n=12, img_size=640, metric_all=0.281/0.716-mean/best, past_thr=0.469-mean: 9,11, 21,19, 17,41, 43,32, 39,70, 86,64, 65,131, 134,130, 120,265, 282,180, 247,354, 512,387 15 | anchors_p6_640: 16 | - [9,11, 21,19, 17,41] # P3/8 17 | - [43,32, 39,70, 86,64] # P4/16 18 | - [65,131, 134,130, 120,265] # P5/32 19 | - [282,180, 247,354, 512,387] # P6/64 20 | 21 | # P6-1280: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1280, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 19,27, 44,40, 38,94, 96,68, 86,152, 180,137, 140,301, 303,264, 238,542, 436,615, 739,380, 925,792 22 | anchors_p6_1280: 23 | - [19,27, 44,40, 38,94] # P3/8 24 | - [96,68, 86,152, 180,137] # P4/16 25 | - [140,301, 303,264, 238,542] # P5/32 26 | - [436,615, 739,380, 925,792] # P6/64 27 | 28 | # P6-1920: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1920, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 28,41, 67,59, 57,141, 144,103, 129,227, 270,205, 209,452, 455,396, 358,812, 653,922, 1109,570, 1387,1187 29 | anchors_p6_1920: 30 | - [28,41, 67,59, 57,141] # P3/8 31 | - [144,103, 129,227, 270,205] # P4/16 32 | - [209,452, 455,396, 358,812] # P5/32 33 | - [653,922, 1109,570, 1387,1187] # P6/64 34 | 35 | 36 | # P7 ------------------------------------------------------------------------------------------------------------------- 37 | # P7-640: thr=0.25: 0.9962 BPR, 6.76 anchors past thr, n=15, img_size=640, metric_all=0.275/0.733-mean/best, past_thr=0.466-mean: 11,11, 13,30, 29,20, 30,46, 61,38, 39,92, 78,80, 146,66, 79,163, 149,150, 321,143, 157,303, 257,402, 359,290, 524,372 38 | anchors_p7_640: 39 | - [11,11, 13,30, 29,20] # P3/8 40 | - [30,46, 61,38, 39,92] # P4/16 41 | - [78,80, 146,66, 79,163] # P5/32 42 | - [149,150, 321,143, 157,303] # P6/64 43 | - [257,402, 359,290, 524,372] # P7/128 44 | 45 | # P7-1280: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1280, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 19,22, 54,36, 32,77, 70,83, 138,71, 75,173, 165,159, 148,334, 375,151, 334,317, 251,626, 499,474, 750,326, 534,814, 1079,818 46 | anchors_p7_1280: 47 | - [19,22, 54,36, 32,77] # P3/8 48 | - [70,83, 138,71, 75,173] # P4/16 49 | - [165,159, 148,334, 375,151] # P5/32 50 | - [334,317, 251,626, 499,474] # P6/64 51 | - [750,326, 534,814, 1079,818] # P7/128 52 | 53 | # P7-1920: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1920, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 29,34, 81,55, 47,115, 105,124, 207,107, 113,259, 247,238, 222,500, 563,227, 501,476, 376,939, 749,711, 1126,489, 801,1222, 1618,1227 54 | anchors_p7_1920: 55 | - [29,34, 81,55, 47,115] # P3/8 56 | - [105,124, 207,107, 113,259] # P4/16 57 | - [247,238, 222,500, 563,227] # P5/32 58 | - [501,476, 376,939, 749,711] # P6/64 59 | - [1126,489, 801,1222, 1618,1227] # P7/128 60 | -------------------------------------------------------------------------------- /EigenCAM.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | warnings.filterwarnings('ignore') 3 | warnings.simplefilter('ignore') 4 | import torch 5 | import cv2 6 | import numpy as np 7 | import requests 8 | import torchvision.transforms as transforms 9 | from pytorch_grad_cam import EigenCAM 10 | from pytorch_grad_cam.utils.image import show_cam_on_image, scale_cam_image 11 | from PIL import Image 12 | 13 | COLORS = np.random.uniform(0, 255, size=(80, 3)) 14 | 15 | def parse_detections(results): 16 | detections = results.pandas().xyxy[0] 17 | detections = detections.to_dict() 18 | boxes, colors, names = [], [], [] 19 | 20 | for i in range(len(detections["xmin"])): 21 | confidence = detections["confidence"][i] 22 | if confidence < 0.2: 23 | continue 24 | xmin = int(detections["xmin"][i]) 25 | ymin = int(detections["ymin"][i]) 26 | xmax = int(detections["xmax"][i]) 27 | ymax = int(detections["ymax"][i]) 28 | name = detections["name"][i] 29 | category = int(detections["class"][i]) 30 | color = COLORS[category] 31 | 32 | boxes.append((xmin, ymin, xmax, ymax)) 33 | colors.append(color) 34 | names.append(name) 35 | return boxes, colors, names 36 | 37 | 38 | def draw_detections(boxes, colors, names, img): 39 | for box, color, name in zip(boxes, colors, names): 40 | xmin, ymin, xmax, ymax = box 41 | cv2.rectangle( 42 | img, 43 | (xmin, ymin), 44 | (xmax, ymax), 45 | color, 46 | 2) 47 | 48 | cv2.putText(img, name, (xmin, ymin - 5), 49 | cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2, 50 | lineType=cv2.LINE_AA) 51 | return img 52 | 53 | 54 | image_url = "https://upload.wikimedia.org/wikipedia/commons/f/f1/Puppies_%284984818141%29.jpg" 55 | img = np.array(Image.open(requests.get(image_url, stream=True).raw)) 56 | img = cv2.resize(img, (640, 640)) 57 | rgb_img = img.copy() 58 | img = np.float32(img) / 255 59 | transform = transforms.ToTensor() 60 | tensor = transform(img).unsqueeze(0) 61 | 62 | model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) 63 | model.eval() 64 | model.cpu() 65 | target_layers = [model.model.model.model[-2]] 66 | 67 | results = model([rgb_img]) 68 | boxes, colors, names = parse_detections(results) 69 | detections = draw_detections(boxes, colors, names, rgb_img.copy()) 70 | Image.fromarray(detections) 71 | 72 | cam = EigenCAM(model, target_layers, use_cuda=False) 73 | grayscale_cam = cam(tensor)[0, :, :] 74 | cam_image = show_cam_on_image(img, grayscale_cam, use_rgb=True) 75 | Image.fromarray(cam_image) 76 | 77 | 78 | def renormalize_cam_in_bounding_boxes(boxes, colors, names, image_float_np, grayscale_cam): 79 | """Normalize the CAM to be in the range [0, 1] 80 | inside every bounding boxes, and zero outside of the bounding boxes. """ 81 | renormalized_cam = np.zeros(grayscale_cam.shape, dtype=np.float32) 82 | for x1, y1, x2, y2 in boxes: 83 | renormalized_cam[y1:y2, x1:x2] = scale_cam_image(grayscale_cam[y1:y2, x1:x2].copy()) 84 | renormalized_cam = scale_cam_image(renormalized_cam) 85 | eigencam_image_renormalized = show_cam_on_image(image_float_np, renormalized_cam, use_rgb=True) 86 | image_with_bounding_boxes = draw_detections(boxes, colors, names, eigencam_image_renormalized) 87 | return image_with_bounding_boxes 88 | 89 | 90 | renormalized_cam_image = renormalize_cam_in_bounding_boxes(boxes, colors, names, img, grayscale_cam) 91 | Image.fromarray(renormalized_cam_image) 92 | 93 | Image.fromarray(np.hstack((rgb_img, cam_image, renormalized_cam_image))) -------------------------------------------------------------------------------- /tools/show_xml/show_xml_on2500.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | import cv2 3 | import os 4 | from tqdm import tqdm 5 | 6 | anthracnose_runner = 0 7 | gray_mold = 0 8 | blossom_blight = 0 9 | leaf_spot = 0 10 | powdery_mildew_fruit = 0 11 | powdery_mildew_leaf = 0 12 | anthracnose_fruit_rot = 0 13 | angular_leafspot = 0 14 | 15 | color = [255,255,255] 16 | 17 | folder = 'dataset_test' 18 | xml_path = '/Users/Dong/Desktop/test2500/' + folder + '/test_xml' # 你的xml文件路径 19 | img_path = '/Users/Dong/Desktop/test2500/' + folder + '/detect' # 图像路径 20 | img_xml = '/Users/Dong/Desktop/test2500/' + folder + '/show_test' # 显示标注框保存该文件的路径 21 | for name in tqdm(os.listdir(xml_path)): 22 | image_name = os.path.join(img_path, name.split('.')[0] + '.jpg') 23 | 24 | if os.path.exists(image_name): 25 | # 打开xml文档 26 | tree = ET.parse(os.path.join(xml_path,name)) 27 | img = cv2.imread(image_name) 28 | box_thickness = int((img.shape[0] + img.shape[1])/1000) + 1 # 标注框的一个参数。本人图像大小不一致,在不同大小的图像上展示不同粗细的bbox 29 | 30 | text_thickness = box_thickness 31 | text_size = float(text_thickness/2) # 显示标注类别的参数。字体大小。这些不是重点。不想要可以删掉。 32 | font = cv2.FONT_HERSHEY_SIMPLEX 33 | 34 | # 得到文档元素对象 35 | root = tree.getroot() 36 | allObjects = root.findall('object') 37 | if len(allObjects) == 0: 38 | print("1 :", name) 39 | _image_name = str(image_name) 40 | _xml_name = _image_name.replace('.jpg', '.xml') 41 | _xml_name = _xml_name.replace('train', 'train_xml') 42 | if os.path.exists(_image_name): 43 | os.remove(_image_name) 44 | else: 45 | print("=========", _image_name) 46 | if os.path.exists(_xml_name): 47 | os.remove(_xml_name) 48 | else: 49 | print("=========", _xml_name) 50 | continue 51 | 52 | for i in range(len(allObjects)): # 遍历xml标签,画框并显示类别。 53 | object = allObjects[i] 54 | objectName = object.find('name').text 55 | 56 | if objectName in ["Angular Leafspot", "Anthracnose Fruit Rot", "Blossom Blight", "Gray Mold", 57 | "Leaf Spot", "Powdery Mildew Fruit", "Powdery Mildew Leaf"]: 58 | xmin = int(object.find('bndbox').find('xmin').text) 59 | ymin = int(object.find('bndbox').find('ymin').text) 60 | xmax = int(object.find('bndbox').find('xmax').text) 61 | ymax = int(object.find('bndbox').find('ymax').text) 62 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 63 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 64 | angular_leafspot+=1 65 | 66 | if objectName not in ["Angular Leafspot", "Anthracnose Fruit Rot", "Blossom Blight", "Gray Mold", 67 | "Leaf Spot", "Powdery Mildew Fruit", "Powdery Mildew Leaf"]: 68 | xmin = int(object.find('bndbox').find('xmin').text) 69 | ymin = int(object.find('bndbox').find('ymin').text) 70 | xmax = int(object.find('bndbox').find('xmax').text) 71 | ymax = int(object.find('bndbox').find('ymax').text) 72 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (0, 0, 255), text_thickness) 73 | cv2.rectangle(img, (xmin, ymin), (xmax, ymax), [64, 128, 256], box_thickness) 74 | print('objectName not in these labels. It is :', objectName) 75 | 76 | if len(allObjects) == 0: 77 | print("error") 78 | 79 | name = name.replace('xml', 'jpg') 80 | img_save_path = os.path.join(img_xml, name) 81 | print(img_save_path) 82 | cv2.imwrite(img_save_path, img) 83 | -------------------------------------------------------------------------------- /tools/process_images/split_the_datasets.py: -------------------------------------------------------------------------------- 1 | import os # os是用来切换路径和创建文件夹的。 2 | import random 3 | from shutil import copy # shutil 是用来复制黏贴文件的 4 | 5 | # "blossom_end_rot", "graymold", "powdery_mildew", "spider_mite", "spotting_disease" 6 | 7 | img_path = "/Users/jiuqingdong/Documents/project_paper_code_data/yolov5-official_new/datasets/tomato_aug/JPEGImages" 8 | labels_path = "/Users/jiuqingdong/Documents/project_paper_code_data/yolov5-official_new/datasets/tomato_aug/Annotations" 9 | 10 | filelist_img_name = [] 11 | filelist_label_name = [] 12 | 13 | for root, dir, filenames in os.walk(img_path): 14 | for filename in filenames: 15 | if '.jpg' in filename: 16 | file_path = os.path.join(root, filename) 17 | print(file_path) 18 | filelist_img_name.append(file_path) 19 | else: 20 | pass 21 | # print("it is not a jpg file.", filename) 22 | 23 | # .xml是我的标注文件的后缀名,您修改为自己的xml或者json或者其他类型即可。 24 | for root, dir, filenames in os.walk(labels_path): 25 | for filename in filenames: 26 | if '.xml' in filename: 27 | filelist_label_name.append(filename.split('.xml')[0]) 28 | else: 29 | pass 30 | # print("it is not a label file.", filename) 31 | 32 | print(len(filelist_img_name)) 33 | print(len(filelist_label_name)) 34 | length = len(filelist_img_name) 35 | a = int(length*0.8) 36 | b = int(length*0.9) 37 | # 我一共含有580张图片,因此 348 ,464,464-580这几个数字是我自己计算的。我的比例是60% 20% 20% 38 | # 如果您有10000张图片和10000张标注,那么您可以设置为6000,8000,8000:,以此类推 39 | # 我没有写自动创建文件夹的那部分代码,因此你要自己首先把相关文件夹创建好。 40 | random.shuffle(filelist_img_name) 41 | filelist_img_name = filelist_img_name 42 | 43 | train_images_path = '/Users/jiuqingdong/Documents/project_paper_code_data/yolov5-official_new/datasets/tomato_aug/images/train/' 44 | train_labels_path = '/Users/jiuqingdong/Documents/project_paper_code_data/yolov5-official_new/datasets/tomato_aug/labels/train/' 45 | val_images_path = '/Users/jiuqingdong/Documents/project_paper_code_data/yolov5-official_new/datasets/tomato_aug/images/val/' 46 | val_labels_path = '/Users/jiuqingdong/Documents/project_paper_code_data/yolov5-official_new/datasets/tomato_aug/labels/val/' 47 | test_images_path = '/Users/jiuqingdong/Documents/project_paper_code_data/yolov5-official_new/datasets/tomato_aug/images/test/' 48 | test_labels_path = '/Users/jiuqingdong/Documents/project_paper_code_data/yolov5-official_new/datasets/tomato_aug/labels/test/' 49 | if not os.path.exists(train_images_path): 50 | os.mkdir(train_images_path) 51 | if not os.path.exists(train_labels_path): 52 | os.mkdir(train_labels_path) 53 | if not os.path.exists(val_images_path): 54 | os.mkdir(val_images_path) 55 | if not os.path.exists(val_labels_path): 56 | os.mkdir(val_labels_path) 57 | if not os.path.exists(test_images_path): 58 | os.mkdir(test_images_path) 59 | if not os.path.exists(test_labels_path): 60 | os.mkdir(test_labels_path) 61 | 62 | for tr in filelist_img_name[0:a]: 63 | copy(tr, train_images_path) 64 | print(tr) 65 | tr = tr.replace('JPEGImages/', 'Annotations/') 66 | tr = tr.replace('.jpg', '.xml') 67 | copy(tr, train_labels_path) 68 | 69 | for val in filelist_img_name[a:b]: 70 | copy(val, val_images_path) 71 | val = val.replace('JPEGImages/', 'Annotations/') 72 | val = val.replace('.jpg', '.xml') 73 | copy(val, val_labels_path) 74 | 75 | for test in filelist_img_name[b:]: 76 | copy(test, test_images_path) 77 | test = test.replace('JPEGImages/', 'Annotations/') 78 | test = test.replace('.jpg', '.xml') 79 | copy(test, test_labels_path) 80 | 81 | ''' 82 | # matching the images to labels 83 | 84 | for k in filelist_img_name: 85 | if k not in filelist_json_name: 86 | #print("there is no label of image {}".format(k)) 87 | pass 88 | else: 89 | print("ok") 90 | 91 | ''' 92 | -------------------------------------------------------------------------------- /utils/activations.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Activation functions 4 | """ 5 | 6 | import torch 7 | import torch.nn as nn 8 | import torch.nn.functional as F 9 | 10 | 11 | # SiLU https://arxiv.org/pdf/1606.08415.pdf ---------------------------------------------------------------------------- 12 | class SiLU(nn.Module): # export-friendly version of nn.SiLU() 13 | @staticmethod 14 | def forward(x): 15 | return x * torch.sigmoid(x) 16 | 17 | 18 | class Hardswish(nn.Module): # export-friendly version of nn.Hardswish() 19 | @staticmethod 20 | def forward(x): 21 | # return x * F.hardsigmoid(x) # for TorchScript and CoreML 22 | return x * F.hardtanh(x + 3, 0.0, 6.0) / 6.0 # for TorchScript, CoreML and ONNX 23 | 24 | 25 | # Mish https://github.com/digantamisra98/Mish -------------------------------------------------------------------------- 26 | class Mish(nn.Module): 27 | @staticmethod 28 | def forward(x): 29 | return x * F.softplus(x).tanh() 30 | 31 | 32 | class MemoryEfficientMish(nn.Module): 33 | class F(torch.autograd.Function): 34 | @staticmethod 35 | def forward(ctx, x): 36 | ctx.save_for_backward(x) 37 | return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x))) 38 | 39 | @staticmethod 40 | def backward(ctx, grad_output): 41 | x = ctx.saved_tensors[0] 42 | sx = torch.sigmoid(x) 43 | fx = F.softplus(x).tanh() 44 | return grad_output * (fx + x * sx * (1 - fx * fx)) 45 | 46 | def forward(self, x): 47 | return self.F.apply(x) 48 | 49 | 50 | # FReLU https://arxiv.org/abs/2007.11824 ------------------------------------------------------------------------------- 51 | class FReLU(nn.Module): 52 | def __init__(self, c1, k=3): # ch_in, kernel 53 | super().__init__() 54 | self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1, bias=False) 55 | self.bn = nn.BatchNorm2d(c1) 56 | 57 | def forward(self, x): 58 | return torch.max(x, self.bn(self.conv(x))) 59 | 60 | 61 | # ACON https://arxiv.org/pdf/2009.04759.pdf ---------------------------------------------------------------------------- 62 | class AconC(nn.Module): 63 | r""" ACON activation (activate or not). 64 | AconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is a learnable parameter 65 | according to "Activate or Not: Learning Customized Activation" . 66 | """ 67 | 68 | def __init__(self, c1): 69 | super().__init__() 70 | self.p1 = nn.Parameter(torch.randn(1, c1, 1, 1)) 71 | self.p2 = nn.Parameter(torch.randn(1, c1, 1, 1)) 72 | self.beta = nn.Parameter(torch.ones(1, c1, 1, 1)) 73 | 74 | def forward(self, x): 75 | dpx = (self.p1 - self.p2) * x 76 | return dpx * torch.sigmoid(self.beta * dpx) + self.p2 * x 77 | 78 | 79 | class MetaAconC(nn.Module): 80 | r""" ACON activation (activate or not). 81 | MetaAconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is generated by a small network 82 | according to "Activate or Not: Learning Customized Activation" . 83 | """ 84 | 85 | def __init__(self, c1, k=1, s=1, r=16): # ch_in, kernel, stride, r 86 | super().__init__() 87 | c2 = max(r, c1 // r) 88 | self.p1 = nn.Parameter(torch.randn(1, c1, 1, 1)) 89 | self.p2 = nn.Parameter(torch.randn(1, c1, 1, 1)) 90 | self.fc1 = nn.Conv2d(c1, c2, k, s, bias=True) 91 | self.fc2 = nn.Conv2d(c2, c1, k, s, bias=True) 92 | # self.bn1 = nn.BatchNorm2d(c2) 93 | # self.bn2 = nn.BatchNorm2d(c1) 94 | 95 | def forward(self, x): 96 | y = x.mean(dim=2, keepdims=True).mean(dim=3, keepdims=True) 97 | # batch-size 1 bug/instabilities https://github.com/ultralytics/yolov5/issues/2891 98 | # beta = torch.sigmoid(self.bn2(self.fc2(self.bn1(self.fc1(y))))) # bug/unstable 99 | beta = torch.sigmoid(self.fc2(self.fc1(y))) # bug patch BN layers removed 100 | dpx = (self.p1 - self.p2) * x 101 | return dpx * torch.sigmoid(beta * dpx) + self.p2 * x 102 | -------------------------------------------------------------------------------- /tools/process_labels/Json2xml.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import json 3 | import os 4 | 5 | 6 | def transfer_category(classes): 7 | category = None 8 | if classes == 'a9': 9 | category = 'powdery_mildew' 10 | elif classes == 'a10': 11 | category = 'powdery_mildew' 12 | elif classes == 'b3': 13 | category = 'phy_disorder' 14 | elif classes == 'b6': 15 | category = 'phy_disorder' 16 | elif classes == 'b7': 17 | category = 'phy_disorder' 18 | elif classes == 'b8': 19 | category = 'phy_disorder' 20 | elif classes == '00': 21 | category = 'normal_fruit' 22 | elif classes == 'c9': 23 | category = 'chem_damage' 24 | else: 25 | print(classes) 26 | print("wrong!!!!!!!!!!!!!!!!!!") 27 | return category 28 | 29 | 30 | def read_json(json_file): 31 | with open(json_file, 'r') as jf: 32 | jf_data = json.load(jf) 33 | name = jf_data['description']['image'] 34 | height = jf_data['description']['height'] 35 | width = jf_data['description']['width'] 36 | if len(jf_data['annotations']['bbox']) != 1: 37 | print(json_file) 38 | print(len(jf_data['annotations']['bbox'])) 39 | xmin = int(jf_data['annotations']['bbox'][0]['x']) 40 | ymin = int(jf_data['annotations']['bbox'][0]['y']) 41 | xmax = int(jf_data['annotations']['bbox'][0]['w'] + xmin) 42 | ymax = int(jf_data['annotations']['bbox'][0]['h'] +ymin) 43 | category = str(jf_data['annotations']['disease']) 44 | category = transfer_category(classes = category) 45 | # print(name, xmin, ymin, xmax, ymax, category,height,width) 46 | return name, xmin, ymin, xmax, ymax, category,height,width 47 | 48 | 49 | def xml_write(xml_file, json_file): 50 | jpg_file = json_file.replace('.json', '.jpg') 51 | jpg_file = jpg_file.replace('/label', '/image') 52 | name, xmin, ymin, xmax, ymax, category, height, width = read_json(json_file) 53 | if category not in ['powdery_mildew', 54 | 'phy_disorder', 55 | 'chem_damage', 56 | 'normal_fruit']: 57 | print(category) 58 | print(json_file) 59 | with codecs.open(xml_file, "w", "utf-8") as xml: 60 | xml.write('\n') 61 | xml.write('\t'+'test'+'\n') 62 | xml.write('\t'+name+'\n') 63 | xml.write('\t'+jpg_file+'\n') 64 | xml.write('\t\n') 65 | xml.write('\t\tUnknown\n') 66 | xml.write('\t\n') 67 | xml.write('\t\n') 68 | xml.write('\t\t'+str(width)+'\n') 69 | xml.write('\t\t'+str(height)+'\n') 70 | xml.write('\t\t3\n') 71 | xml.write('\t\n') 72 | xml.write('\t0\n') 73 | if xmax<=xmin: 74 | pass 75 | elif ymax<=ymin: 76 | pass 77 | else: 78 | xml.write('\t\n') 79 | xml.write('\t\t'+category+'\n') 80 | xml.write('\t\tUnspecified\n') 81 | xml.write('\t\t0\n') 82 | xml.write('\t\t0\n') 83 | xml.write('\t\t\n') 84 | xml.write('\t\t\t' + str(xmin) + '\n') 85 | xml.write('\t\t\t' + str(ymin) + '\n') 86 | xml.write('\t\t\t' + str(xmax) + '\n') 87 | xml.write('\t\t\t' + str(ymax) + '\n') 88 | xml.write('\t\t\n') 89 | xml.write('\t\n') 90 | xml.write('') 91 | 92 | def main(json_fold = '/Volumes/DATA/paprika_label/'): 93 | for roots, dirs, files in os.walk(json_fold): 94 | for file in files: 95 | if file.endswith('.json'): 96 | json_file = os.path.join(roots, file) 97 | xml_roots = roots.replace('label', 'xml') 98 | if not os.path.exists(xml_roots): 99 | os.mkdir(xml_roots) 100 | name = file.split('.')[0]+'.xml' 101 | xml_file = os.path.join(xml_roots, name) 102 | xml_write(xml_file, json_file) 103 | 104 | main() -------------------------------------------------------------------------------- /tools/process_labels/Json2xml_.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import json 3 | import os 4 | 5 | 6 | def transfer_category(classes): 7 | category = None 8 | if classes == 'a9': 9 | category = 'powdery_mildew' 10 | elif classes == 'a10': 11 | category = 'powdery_mildew' 12 | elif classes == 'b3': 13 | category = 'phy_disorder' 14 | elif classes == 'b6': 15 | category = 'phy_disorder' 16 | elif classes == 'b7': 17 | category = 'phy_disorder' 18 | elif classes == 'b8': 19 | category = 'phy_disorder' 20 | elif classes == '00': 21 | category = 'normal_fruit' 22 | elif classes == 'c9': 23 | category = 'chem_damage' 24 | else: 25 | print(classes) 26 | print("wrong!!!!!!!!!!!!!!!!!!") 27 | return category 28 | 29 | 30 | def read_json(json_file): 31 | with open(json_file, 'r') as jf: 32 | jf_data = json.load(jf) 33 | name = jf_data['description']['image'] 34 | height = jf_data['description']['height'] 35 | width = jf_data['description']['width'] 36 | if len(jf_data['annotations']['bbox']) != 1: 37 | print(json_file) 38 | print(len(jf_data['annotations']['bbox'])) 39 | xmin = int(jf_data['annotations']['bbox'][0]['x']) 40 | ymin = int(jf_data['annotations']['bbox'][0]['y']) 41 | xmax = int(jf_data['annotations']['bbox'][0]['w'] + xmin) 42 | ymax = int(jf_data['annotations']['bbox'][0]['h'] +ymin) 43 | category = str(jf_data['annotations']['disease']) 44 | category = transfer_category(classes = category) 45 | # print(name, xmin, ymin, xmax, ymax, category,height,width) 46 | return name, xmin, ymin, xmax, ymax, category,height,width 47 | 48 | 49 | def xml_write(xml_file, json_file): 50 | jpg_file = json_file.replace('.json', '.jpg') 51 | jpg_file = jpg_file.replace('/label', '/image') 52 | name, xmin, ymin, xmax, ymax, category, height, width = read_json(json_file) 53 | if category not in ['powdery_mildew', 54 | 'phy_disorder', 55 | 'chem_damage', 56 | 'normal_fruit']: 57 | print(category) 58 | print(json_file) 59 | with codecs.open(xml_file, "w", "utf-8") as xml: 60 | xml.write('\n') 61 | xml.write('\t'+'test'+'\n') 62 | xml.write('\t'+name+'\n') 63 | xml.write('\t'+jpg_file+'\n') 64 | xml.write('\t\n') 65 | xml.write('\t\tUnknown\n') 66 | xml.write('\t\n') 67 | xml.write('\t\n') 68 | xml.write('\t\t'+str(width)+'\n') 69 | xml.write('\t\t'+str(height)+'\n') 70 | xml.write('\t\t3\n') 71 | xml.write('\t\n') 72 | xml.write('\t0\n') 73 | if xmax<=xmin: 74 | pass 75 | elif ymax<=ymin: 76 | pass 77 | else: 78 | xml.write('\t\n') 79 | xml.write('\t\t'+category+'\n') 80 | xml.write('\t\tUnspecified\n') 81 | xml.write('\t\t0\n') 82 | xml.write('\t\t0\n') 83 | xml.write('\t\t\n') 84 | xml.write('\t\t\t' + str(xmin) + '\n') 85 | xml.write('\t\t\t' + str(ymin) + '\n') 86 | xml.write('\t\t\t' + str(xmax) + '\n') 87 | xml.write('\t\t\t' + str(ymax) + '\n') 88 | xml.write('\t\t\n') 89 | xml.write('\t\n') 90 | xml.write('') 91 | 92 | def main(json_fold = '/Users/jiuqingdong/Desktop/dataset_examples/strawberry/labels/'): 93 | for roots, dirs, files in os.walk(json_fold): 94 | for file in files: 95 | if file.endswith('.json'): 96 | json_file = os.path.join(roots, file) 97 | xml_roots = roots.replace('labels', 'xmls') 98 | print(xml_roots) 99 | if not os.path.exists(xml_roots): 100 | os.mkdir(xml_roots) 101 | name = file.split('.')[0]+'.xml' 102 | xml_file = os.path.join(xml_roots, name) 103 | xml_write(xml_file, json_file) 104 | 105 | main() -------------------------------------------------------------------------------- /tools/process_labels/Json2xml_paprika.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import json 3 | import os 4 | 5 | 6 | def transfer_category(classes): 7 | category = None 8 | if classes == 'a9': 9 | category = 'powdery_mildew' 10 | elif classes == 'a10': 11 | category = 'powdery_mildew' 12 | elif classes == 'b3': 13 | category = 'phy_disorder' 14 | elif classes == 'b6': 15 | category = 'phy_disorder' 16 | elif classes == 'b7': 17 | category = 'phy_disorder' 18 | elif classes == 'b8': 19 | category = 'phy_disorder' 20 | elif classes == '00': 21 | category = 'normal_fruit' 22 | elif classes == 'c9': 23 | category = 'chem_damage' 24 | else: 25 | print(classes) 26 | print("wrong!!!!!!!!!!!!!!!!!!") 27 | return category 28 | 29 | 30 | def read_json(json_file): 31 | with open(json_file, 'r') as jf: 32 | jf_data = json.load(jf) 33 | name = jf_data['description']['image'] 34 | height = jf_data['description']['height'] 35 | width = jf_data['description']['width'] 36 | if len(jf_data['annotations']['bbox']) != 1: 37 | print(json_file) 38 | print(len(jf_data['annotations']['bbox'])) 39 | xmin = int(jf_data['annotations']['bbox'][0]['x']) 40 | ymin = int(jf_data['annotations']['bbox'][0]['y']) 41 | xmax = int(jf_data['annotations']['bbox'][0]['w'] + xmin) 42 | ymax = int(jf_data['annotations']['bbox'][0]['h'] +ymin) 43 | category = str(jf_data['annotations']['disease']) 44 | category = transfer_category(classes = category) 45 | # print(name, xmin, ymin, xmax, ymax, category,height,width) 46 | return name, xmin, ymin, xmax, ymax, category,height,width 47 | 48 | 49 | def xml_write(xml_file, json_file): 50 | jpg_file = json_file.replace('.json', '.jpg') 51 | jpg_file = jpg_file.replace('/labels', '/images') 52 | name, xmin, ymin, xmax, ymax, category, height, width = read_json(json_file) 53 | if category not in ['powdery_mildew', 54 | 'phy_disorder', 55 | 'chem_damage', 56 | 'normal_fruit']: 57 | print(category) 58 | print(json_file) 59 | with codecs.open(xml_file, "w", "utf-8") as xml: 60 | xml.write('\n') 61 | xml.write('\t'+'test'+'\n') 62 | xml.write('\t'+name+'\n') 63 | xml.write('\t'+jpg_file+'\n') 64 | xml.write('\t\n') 65 | xml.write('\t\tUnknown\n') 66 | xml.write('\t\n') 67 | xml.write('\t\n') 68 | xml.write('\t\t'+str(width)+'\n') 69 | xml.write('\t\t'+str(height)+'\n') 70 | xml.write('\t\t3\n') 71 | xml.write('\t\n') 72 | xml.write('\t0\n') 73 | if xmax<=xmin: 74 | pass 75 | elif ymax<=ymin: 76 | pass 77 | else: 78 | xml.write('\t\n') 79 | xml.write('\t\t'+category+'\n') 80 | xml.write('\t\tUnspecified\n') 81 | xml.write('\t\t0\n') 82 | xml.write('\t\t0\n') 83 | xml.write('\t\t\n') 84 | xml.write('\t\t\t' + str(xmin) + '\n') 85 | xml.write('\t\t\t' + str(ymin) + '\n') 86 | xml.write('\t\t\t' + str(xmax) + '\n') 87 | xml.write('\t\t\t' + str(ymax) + '\n') 88 | xml.write('\t\t\n') 89 | xml.write('\t\n') 90 | xml.write('') 91 | 92 | def main(json_fold = '/Users/jiuqingdong/Desktop/dataset_examples/paprika/labels/'): 93 | for roots, dirs, files in os.walk(json_fold): 94 | for file in files: 95 | if file.endswith('.json'): 96 | json_file = os.path.join(roots, file) 97 | xml_roots = roots.replace('labels', 'xmls') 98 | print(xml_roots) 99 | if not os.path.exists(xml_roots): 100 | os.mkdir(xml_roots) 101 | name = file.split('.')[0]+'.xml' 102 | xml_file = os.path.join(xml_roots, name) 103 | xml_write(xml_file, json_file) 104 | 105 | main() -------------------------------------------------------------------------------- /tools/process_labels/Json2xml_strawberry.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import json 3 | import os 4 | 5 | 6 | def transfer_category(classes): 7 | category = None 8 | if classes == 'a9': 9 | category = 'powdery_mildew' 10 | elif classes == 'a10': 11 | category = 'powdery_mildew' 12 | elif classes == 'b3': 13 | category = 'phy_disorder' 14 | elif classes == 'b6': 15 | category = 'phy_disorder' 16 | elif classes == 'b7': 17 | category = 'phy_disorder' 18 | elif classes == 'b8': 19 | category = 'phy_disorder' 20 | elif classes == '00': 21 | category = 'normal_fruit' 22 | elif classes == 'c9': 23 | category = 'chem_damage' 24 | else: 25 | print(classes) 26 | category = 'x' 27 | print("wrong!!!!!!!!!!!!!!!!!!") 28 | return category 29 | 30 | 31 | def read_json(json_file): 32 | with open(json_file, 'r') as jf: 33 | jf_data = json.load(jf) 34 | name = jf_data['description']['image'] 35 | height = jf_data['description']['height'] 36 | width = jf_data['description']['width'] 37 | if len(jf_data['annotations']['bbox']) != 1: 38 | print(json_file) 39 | print(len(jf_data['annotations']['bbox'])) 40 | xmin = int(jf_data['annotations']['bbox'][0]['x']) 41 | ymin = int(jf_data['annotations']['bbox'][0]['y']) 42 | xmax = int(jf_data['annotations']['bbox'][0]['w'] + xmin) 43 | ymax = int(jf_data['annotations']['bbox'][0]['h'] +ymin) 44 | category = str(jf_data['annotations']['disease']) 45 | category = transfer_category(classes = category) 46 | # print(name, xmin, ymin, xmax, ymax, category,height,width) 47 | return name, xmin, ymin, xmax, ymax, category,height,width 48 | 49 | 50 | def xml_write(xml_file, json_file): 51 | jpg_file = json_file.replace('.json', '.jpg') 52 | jpg_file = jpg_file.replace('/label', '/image') 53 | name, xmin, ymin, xmax, ymax, category, height, width = read_json(json_file) 54 | if category not in ['powdery_mildew', 55 | 'phy_disorder', 56 | 'chem_damage', 57 | 'normal_fruit']: 58 | print(category) 59 | print(json_file) 60 | with codecs.open(xml_file, "w", "utf-8") as xml: 61 | xml.write('\n') 62 | xml.write('\t'+'test'+'\n') 63 | xml.write('\t'+name+'\n') 64 | xml.write('\t'+jpg_file+'\n') 65 | xml.write('\t\n') 66 | xml.write('\t\tUnknown\n') 67 | xml.write('\t\n') 68 | xml.write('\t\n') 69 | xml.write('\t\t'+str(width)+'\n') 70 | xml.write('\t\t'+str(height)+'\n') 71 | xml.write('\t\t3\n') 72 | xml.write('\t\n') 73 | xml.write('\t0\n') 74 | if xmax<=xmin: 75 | pass 76 | elif ymax<=ymin: 77 | pass 78 | else: 79 | xml.write('\t\n') 80 | xml.write('\t\t'+category+'\n') 81 | xml.write('\t\tUnspecified\n') 82 | xml.write('\t\t0\n') 83 | xml.write('\t\t0\n') 84 | xml.write('\t\t\n') 85 | xml.write('\t\t\t' + str(xmin) + '\n') 86 | xml.write('\t\t\t' + str(ymin) + '\n') 87 | xml.write('\t\t\t' + str(xmax) + '\n') 88 | xml.write('\t\t\t' + str(ymax) + '\n') 89 | xml.write('\t\t\n') 90 | xml.write('\t\n') 91 | xml.write('') 92 | 93 | def main(json_fold = '/Users/jiuqingdong/Desktop/dataset_examples/strawberry/labels/'): 94 | for roots, dirs, files in os.walk(json_fold): 95 | for file in files: 96 | if file.endswith('.json'): 97 | json_file = os.path.join(roots, file) 98 | xml_roots = roots.replace('labels', 'xmls') 99 | print(xml_roots) 100 | if not os.path.exists(xml_roots): 101 | os.mkdir(xml_roots) 102 | name = file.split('.')[0]+'.xml' 103 | xml_file = os.path.join(xml_roots, name) 104 | xml_write(xml_file, json_file) 105 | 106 | main() -------------------------------------------------------------------------------- /tools/process_labels/Json2xml_tomoto.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import json 3 | import os 4 | 5 | 6 | def transfer_category(classes): 7 | category = None 8 | if classes == 'a9': 9 | category = 'powdery_mildew' 10 | elif classes == 'a10': 11 | category = 'powdery_mildew' 12 | elif classes == 'b3': 13 | category = 'phy_disorder' 14 | elif classes == 'b6': 15 | category = 'phy_disorder' 16 | elif classes == 'b7': 17 | category = 'phy_disorder' 18 | elif classes == 'b8': 19 | category = 'phy_disorder' 20 | elif classes == '00': 21 | category = 'normal_fruit' 22 | elif classes == 'c9': 23 | category = 'chem_damage' 24 | else: 25 | print(classes) 26 | print("wrong!!!!!!!!!!!!!!!!!!") 27 | return category 28 | 29 | 30 | def read_json(json_file): 31 | with open(json_file, 'r') as jf: 32 | jf_data = json.load(jf) 33 | name = jf_data['description']['image'] 34 | height = jf_data['description']['height'] 35 | width = jf_data['description']['width'] 36 | if len(jf_data['annotations']['bbox']) != 1: 37 | print(json_file) 38 | print(len(jf_data['annotations']['bbox'])) 39 | xmin = int(jf_data['annotations']['bbox'][0]['x']) 40 | ymin = int(jf_data['annotations']['bbox'][0]['y']) 41 | xmax = int(jf_data['annotations']['bbox'][0]['w'] + xmin) 42 | ymax = int(jf_data['annotations']['bbox'][0]['h'] +ymin) 43 | category = str(jf_data['annotations']['disease']) 44 | category = transfer_category(classes = category) 45 | # print(name, xmin, ymin, xmax, ymax, category,height,width) 46 | return name, xmin, ymin, xmax, ymax, category,height,width 47 | 48 | 49 | def xml_write(xml_file, json_file): 50 | jpg_file = json_file.replace('.json', '.jpg') 51 | jpg_file = jpg_file.replace('/label', '/image') 52 | name, xmin, ymin, xmax, ymax, category, height, width = read_json(json_file) 53 | if category not in ['powdery_mildew', 54 | 'phy_disorder', 55 | 'chem_damage', 56 | 'normal_fruit']: 57 | print(category) 58 | print(json_file) 59 | with codecs.open(xml_file, "w", "utf-8") as xml: 60 | xml.write('\n') 61 | xml.write('\t'+'test'+'\n') 62 | xml.write('\t'+name+'\n') 63 | xml.write('\t'+jpg_file+'\n') 64 | xml.write('\t\n') 65 | xml.write('\t\tUnknown\n') 66 | xml.write('\t\n') 67 | xml.write('\t\n') 68 | xml.write('\t\t'+str(width)+'\n') 69 | xml.write('\t\t'+str(height)+'\n') 70 | xml.write('\t\t3\n') 71 | xml.write('\t\n') 72 | xml.write('\t0\n') 73 | if xmax<=xmin: 74 | pass 75 | elif ymax<=ymin: 76 | pass 77 | else: 78 | xml.write('\t\n') 79 | xml.write('\t\t'+category+'\n') 80 | xml.write('\t\tUnspecified\n') 81 | xml.write('\t\t0\n') 82 | xml.write('\t\t0\n') 83 | xml.write('\t\t\n') 84 | xml.write('\t\t\t' + str(xmin) + '\n') 85 | xml.write('\t\t\t' + str(ymin) + '\n') 86 | xml.write('\t\t\t' + str(xmax) + '\n') 87 | xml.write('\t\t\t' + str(ymax) + '\n') 88 | xml.write('\t\t\n') 89 | xml.write('\t\n') 90 | xml.write('') 91 | 92 | def main(json_fold = '/Users/jiuqingdong/Desktop/dataset_examples/tomato/labels/'): 93 | print(json_fold) 94 | for roots, dirs, files in os.walk(json_fold): 95 | for file in files: 96 | print(file) 97 | if file.endswith('.json'): 98 | json_file = os.path.join(roots, file) 99 | xml_roots = roots.replace('labels', 'xmls') 100 | print(xml_roots) 101 | if not os.path.exists(xml_roots): 102 | os.mkdir(xml_roots) 103 | name = file.split('.')[0]+'.xml' 104 | xml_file = os.path.join(xml_roots, name) 105 | xml_write(xml_file, json_file) 106 | 107 | 108 | main() -------------------------------------------------------------------------------- /tools/process_labels/merge_box.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | from lxml import etree 3 | import os 4 | import numpy 5 | 6 | 7 | def get_value(category, xmin, xmax, ymin, ymax): 8 | category = str(category.text) 9 | xmin = int(xmin.text) 10 | xmax = int(xmax.text) 11 | ymin = int(ymin.text) 12 | ymax = int(ymax.text) 13 | 14 | return category, xmin, xmax, ymin, ymax 15 | 16 | 17 | def norm(new_xmin, new_xmax, new_ymin, new_ymax, width, height): 18 | 19 | if new_xmin < 0: 20 | new_xmin = 1 21 | if new_xmax >= width: 22 | new_xmax = width - 1 23 | if new_ymin < 0: 24 | new_ymin = 1 25 | if new_ymax >= height: 26 | new_ymax = height - 1 27 | 28 | return new_xmin, new_xmax, new_ymin, new_ymax 29 | 30 | 31 | def min_max(list): 32 | list = numpy.array(list) 33 | if len(list) == 1: 34 | xmin = list[0,0] 35 | xmax = list[0,1] 36 | ymin = list[0,2] 37 | ymax = list[0,3] 38 | else: 39 | xmin = min(list[:,0]) 40 | xmax = max(list[:,1]) 41 | ymin = min(list[:,2]) 42 | ymax = max(list[:,3]) 43 | return str(xmin),str(xmax),str(ymin),str(ymax) 44 | 45 | def add_node(root,label,xmin,ymin,xmax,ymax): 46 | object = etree.Element("object") 47 | namen = etree.SubElement(object,"name") 48 | namen.text = label 49 | object.append(namen) 50 | pose = etree.SubElement(object,"pose") 51 | pose.text = str(0) 52 | object.append(pose) 53 | truncated = etree.SubElement(object,"truncated") 54 | truncated.text = str(0) 55 | object.append(truncated) 56 | difficult = etree.SubElement(object,"difficult") 57 | difficult.text = str(0) 58 | object.append(difficult) 59 | bndbox = etree.SubElement(object,"bndbox") 60 | xminn = etree.SubElement(bndbox,"xmin") 61 | xminn.text = str(xmin) 62 | bndbox.append(xminn) 63 | yminn = etree.SubElement(bndbox,"ymin") 64 | yminn.text = str(ymin) 65 | bndbox.append(yminn) 66 | xmaxn = etree.SubElement(bndbox,"xmax") 67 | xmaxn.text = str(xmax) 68 | bndbox.append(xmaxn) 69 | ymaxn = etree.SubElement(bndbox,"ymax") 70 | ymaxn.text = str(ymax) 71 | root.getroot().append(object) 72 | 73 | 74 | def merge_xml(xml_file, target): 75 | target_file = xml_file.replace('labels', target) 76 | 77 | #tree = ET.parse(xml_file) 78 | #root = tree.getroot() 79 | 80 | parser = etree.XMLParser(remove_blank_text=True) # 81 | root = etree.parse(xml_file, parser) 82 | tree = etree.ElementTree(root.getroot()) 83 | 84 | node = etree.Element('root') 85 | 86 | 87 | size = root.find('size') 88 | width = int(size.find('width').text) 89 | height = int(size.find('height').text) 90 | 91 | all_objects = {"blossom_end_rot":[], "graymold":[], "spotting_disease":[]} 92 | 93 | for object in tree.findall('object'): 94 | category = object.find('name') 95 | bndbox = object.find('bndbox') # 子节点下节点rank的值 96 | xmin = bndbox.find('xmin') # type(xmin) = int 97 | xmax = bndbox.find('xmax') 98 | ymin = bndbox.find('ymin') 99 | ymax = bndbox.find('ymax') 100 | 101 | old_category, old_xmin, old_xmax, old_ymin, old_ymax = get_value(category, xmin, xmax, ymin, ymax) 102 | 103 | if old_category in ["blossom_end_rot", "graymold", "spotting_disease"]: 104 | all_objects[old_category].append([old_xmin, old_xmax, old_ymin, old_ymax]) 105 | parent = object.getparent() 106 | parent.remove(object) 107 | 108 | for dict in ["blossom_end_rot", "graymold", "spotting_disease"]: 109 | # 110 | if len(all_objects[dict]) == 0: 111 | continue 112 | else: 113 | xmin, xmax, ymin, ymax = min_max(all_objects[dict]) 114 | add_node(root, dict, xmin, ymin, xmax, ymax) 115 | 116 | 117 | tree.write(target_file, pretty_print=True, xml_declaration=False, encoding='utf-8') 118 | 119 | 120 | def run(xml_path= None, target = None): 121 | for roots, dirs, files in os.walk(xml_path): 122 | print(roots) 123 | target_dir = roots.replace('labels', target) 124 | if not os.path.exists(target_dir): 125 | os.mkdir(target_dir) 126 | for file in files: 127 | if file.endswith('.xml'): 128 | file_path = os.path.join(roots, file) 129 | merge_xml(xml_file=file_path, target = target) 130 | 131 | print("Done!") 132 | 133 | 134 | run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_with_semi-global') 135 | 136 | -------------------------------------------------------------------------------- /models/experimental.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Experimental modules 4 | """ 5 | import math 6 | 7 | import numpy as np 8 | import torch 9 | import torch.nn as nn 10 | 11 | from models.common import Conv 12 | from utils.downloads import attempt_download 13 | 14 | 15 | class CrossConv(nn.Module): 16 | # Cross Convolution Downsample 17 | def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False): 18 | # ch_in, ch_out, kernel, stride, groups, expansion, shortcut 19 | super().__init__() 20 | c_ = int(c2 * e) # hidden channels 21 | self.cv1 = Conv(c1, c_, (1, k), (1, s)) 22 | self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g) 23 | self.add = shortcut and c1 == c2 24 | 25 | def forward(self, x): 26 | return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) 27 | 28 | 29 | class Sum(nn.Module): 30 | # Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070 31 | def __init__(self, n, weight=False): # n: number of inputs 32 | super().__init__() 33 | self.weight = weight # apply weights boolean 34 | self.iter = range(n - 1) # iter object 35 | if weight: 36 | self.w = nn.Parameter(-torch.arange(1.0, n) / 2, requires_grad=True) # layer weights 37 | 38 | def forward(self, x): 39 | y = x[0] # no weight 40 | if self.weight: 41 | w = torch.sigmoid(self.w) * 2 42 | for i in self.iter: 43 | y = y + x[i + 1] * w[i] 44 | else: 45 | for i in self.iter: 46 | y = y + x[i + 1] 47 | return y 48 | 49 | 50 | class MixConv2d(nn.Module): 51 | # Mixed Depth-wise Conv https://arxiv.org/abs/1907.09595 52 | def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): # ch_in, ch_out, kernel, stride, ch_strategy 53 | super().__init__() 54 | n = len(k) # number of convolutions 55 | if equal_ch: # equal c_ per group 56 | i = torch.linspace(0, n - 1E-6, c2).floor() # c2 indices 57 | c_ = [(i == g).sum() for g in range(n)] # intermediate channels 58 | else: # equal weight.numel() per group 59 | b = [c2] + [0] * n 60 | a = np.eye(n + 1, n, k=-1) 61 | a -= np.roll(a, 1, axis=1) 62 | a *= np.array(k) ** 2 63 | a[0] = 1 64 | c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b 65 | 66 | self.m = nn.ModuleList( 67 | [nn.Conv2d(c1, int(c_), k, s, k // 2, groups=math.gcd(c1, int(c_)), bias=False) for k, c_ in zip(k, c_)]) 68 | self.bn = nn.BatchNorm2d(c2) 69 | self.act = nn.SiLU() 70 | 71 | def forward(self, x): 72 | return self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) 73 | 74 | 75 | class Ensemble(nn.ModuleList): 76 | # Ensemble of models 77 | def __init__(self): 78 | super().__init__() 79 | 80 | def forward(self, x, augment=False, profile=False, visualize=False): 81 | y = [] 82 | for module in self: 83 | y.append(module(x, augment, profile, visualize)[0]) 84 | # y = torch.stack(y).max(0)[0] # max ensemble 85 | # y = torch.stack(y).mean(0) # mean ensemble 86 | y = torch.cat(y, 1) # nms ensemble 87 | return y, None # inference, train output 88 | 89 | 90 | def attempt_load(weights, map_location=None, inplace=True, fuse=True): 91 | from models.yolo import Detect, Model 92 | 93 | # Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a 94 | model = Ensemble() 95 | for w in weights if isinstance(weights, list) else [weights]: 96 | ckpt = torch.load(attempt_download(w), map_location=map_location) # load 97 | if fuse: 98 | model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model 99 | else: 100 | model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().eval()) # without layer fuse 101 | 102 | # Compatibility updates 103 | for m in model.modules(): 104 | if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU, Detect, Model]: 105 | m.inplace = inplace # pytorch 1.7.0 compatibility 106 | if type(m) is Detect: 107 | if not isinstance(m.anchor_grid, list): # new Detect Layer compatibility 108 | delattr(m, 'anchor_grid') 109 | setattr(m, 'anchor_grid', [torch.zeros(1)] * m.nl) 110 | elif type(m) is Conv: 111 | m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility 112 | 113 | if len(model) == 1: 114 | return model[-1] # return model 115 | else: 116 | print(f'Ensemble created with {weights}\n') 117 | for k in ['names']: 118 | setattr(model, k, getattr(model[-1], k)) 119 | model.stride = model[torch.argmax(torch.tensor([m.stride.max() for m in model])).int()].stride # max stride 120 | return model # return ensemble 121 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to YOLOv5 🚀 2 | 3 | We love your input! We want to make contributing to YOLOv5 as easy and transparent as possible, whether it's: 4 | 5 | - Reporting a bug 6 | - Discussing the current state of the code 7 | - Submitting a fix 8 | - Proposing a new feature 9 | - Becoming a maintainer 10 | 11 | YOLOv5 works so well due to our combined community effort, and for every small improvement you contribute you will be 12 | helping push the frontiers of what's possible in AI 😃! 13 | 14 | ## Submitting a Pull Request (PR) 🛠️ 15 | 16 | Submitting a PR is easy! This example shows how to submit a PR for updating `requirements.txt` in 4 steps: 17 | 18 | ### 1. Select File to Update 19 | 20 | Select `requirements.txt` to update by clicking on it in GitHub. 21 |

PR_step1

22 | 23 | ### 2. Click 'Edit this file' 24 | 25 | Button is in top-right corner. 26 |

PR_step2

27 | 28 | ### 3. Make Changes 29 | 30 | Change `matplotlib` version from `3.2.2` to `3.3`. 31 |

PR_step3

32 | 33 | ### 4. Preview Changes and Submit PR 34 | 35 | Click on the **Preview changes** tab to verify your updates. At the bottom of the screen select 'Create a **new branch** 36 | for this commit', assign your branch a descriptive name such as `fix/matplotlib_version` and click the green **Propose 37 | changes** button. All done, your PR is now submitted to YOLOv5 for review and approval 😃! 38 |

PR_step4

39 | 40 | ### PR recommendations 41 | 42 | To allow your work to be integrated as seamlessly as possible, we advise you to: 43 | 44 | - ✅ Verify your PR is **up-to-date with upstream/master.** If your PR is behind upstream/master an 45 | automatic [GitHub actions](https://github.com/ultralytics/yolov5/blob/master/.github/workflows/rebase.yml) rebase may 46 | be attempted by including the /rebase command in a comment body, or by running the following code, replacing 'feature' 47 | with the name of your local branch: 48 | 49 | ```bash 50 | git remote add upstream https://github.com/ultralytics/yolov5.git 51 | git fetch upstream 52 | git checkout feature # <----- replace 'feature' with local branch name 53 | git merge upstream/master 54 | git push -u origin -f 55 | ``` 56 | 57 | - ✅ Verify all Continuous Integration (CI) **checks are passing**. 58 | - ✅ Reduce changes to the absolute **minimum** required for your bug fix or feature addition. _"It is not daily increase 59 | but daily decrease, hack away the unessential. The closer to the source, the less wastage there is."_ — Bruce Lee 60 | 61 | ## Submitting a Bug Report 🐛 62 | 63 | If you spot a problem with YOLOv5 please submit a Bug Report! 64 | 65 | For us to start investigating a possible problem we need to be able to reproduce it ourselves first. We've created a few 66 | short guidelines below to help users provide what we need in order to get started. 67 | 68 | When asking a question, people will be better able to provide help if you provide **code** that they can easily 69 | understand and use to **reproduce** the problem. This is referred to by community members as creating 70 | a [minimum reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). Your code that reproduces 71 | the problem should be: 72 | 73 | * ✅ **Minimal** – Use as little code as possible that still produces the same problem 74 | * ✅ **Complete** – Provide **all** parts someone else needs to reproduce your problem in the question itself 75 | * ✅ **Reproducible** – Test the code you're about to provide to make sure it reproduces the problem 76 | 77 | In addition to the above requirements, for [Ultralytics](https://ultralytics.com/) to provide assistance your code 78 | should be: 79 | 80 | * ✅ **Current** – Verify that your code is up-to-date with current 81 | GitHub [master](https://github.com/ultralytics/yolov5/tree/master), and if necessary `git pull` or `git clone` a new 82 | copy to ensure your problem has not already been resolved by previous commits. 83 | * ✅ **Unmodified** – Your problem must be reproducible without any modifications to the codebase in this 84 | repository. [Ultralytics](https://ultralytics.com/) does not provide support for custom code ⚠️. 85 | 86 | If you believe your problem meets all of the above criteria, please close this issue and raise a new one using the 🐛 ** 87 | Bug Report** [template](https://github.com/ultralytics/yolov5/issues/new/choose) and providing 88 | a [minimum reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) to help us better 89 | understand and diagnose your problem. 90 | 91 | ## License 92 | 93 | By contributing, you agree that your contributions will be licensed under 94 | the [GPL-3.0 license](https://choosealicense.com/licenses/gpl-3.0/) 95 | -------------------------------------------------------------------------------- /tools/show_xml/show_xml_on_tomato.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | import cv2 3 | import os 4 | from tqdm import tqdm 5 | 6 | xml_path = '/Volumes/Data/project_paper_code_data/Plant_diseases_dataset/tomoto_v3/labels/' # 你的xml文件路径 7 | img_path = '/Volumes/Data/project_paper_code_data/Plant_diseases_dataset/tomoto_v3/JPEGImages/' # 图像路径 8 | img_xml = '/Volumes/Data/project_paper_code_data/Plant_diseases_dataset/tomoto_v3/show_xml/' # 显示标注框保存该文件的路径 9 | 10 | all_class = {"healthy": 0, "canker": 1, "plague": 2, "back": 3, "yculr": 4, "miner": 5, "lmold": 6, "tocv": 7, "stress": 8, "powder": 9, 11 | "gmold": 10, "wfly": 11, "eggfly": 12, "death": 13, "spot": 14,"old": 15, "magdef": 16, "unknown": 17, "wilt": 18} 12 | all_instance = [] 13 | # for set in ['train', 'val', 'test']: 14 | # xml_path = xml_path0 + set +'_xml' 15 | # img_path = img_path0 + set 16 | for name in tqdm(os.listdir(xml_path)): 17 | image_name = os.path.join(img_path, name.split('.')[0] + '.jpg') 18 | if os.path.exists(image_name): 19 | # 打开xml文档 20 | tree = ET.parse(os.path.join(xml_path,name)) 21 | img = cv2.imread(image_name) 22 | box_thickness = int((img.shape[0] + img.shape[1])/1000) + 1 # 标注框的一个参数。本人图像大小不一致,在不同大小的图像上展示不同粗细的bbox 23 | text_thickness = box_thickness 24 | text_size = float(text_thickness/2) # 显示标注类别的参数。字体大小。这些不是重点。不想要可以删掉。 25 | font = cv2.FONT_HERSHEY_SIMPLEX 26 | # 得到文档元素对象 27 | root = tree.getroot() 28 | allObjects = root.findall('object') 29 | if len(allObjects) == 0: 30 | print("1 :", name) 31 | _image_name = str(image_name) 32 | _xml_name = _image_name.replace('.jpg', '.xml') 33 | _xml_name = _xml_name.replace('train', 'train_xml') 34 | if os.path.exists(_image_name): 35 | os.remove(_image_name) 36 | else: 37 | print("=========", _image_name) 38 | if os.path.exists(_xml_name): 39 | os.remove(_xml_name) 40 | else: 41 | print("=========", _xml_name) 42 | continue 43 | for i in range(len(allObjects)): # 遍历xml标签,画框并显示类别。 44 | object = allObjects[i] 45 | objectName = object.find('name').text 46 | all_instance.append(objectName) 47 | xmin = int(object.find('bndbox').find('xmin').text) 48 | ymin = int(object.find('bndbox').find('ymin').text) 49 | xmax = int(object.find('bndbox').find('xmax').text) 50 | ymax = int(object.find('bndbox').find('ymax').text) 51 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (0,0,0), text_thickness) 52 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 53 | if len(allObjects) == 0: 54 | print("error") 55 | name = name.replace('xml', 'jpg') 56 | img_save_path = os.path.join(img_xml, name) 57 | cv2.imwrite(img_save_path, img) 58 | 59 | for key in all_class: 60 | print(key, all_instance.count(key)) 61 | 62 | ''' 63 | for name in tqdm(os.listdir(xml_path0)): 64 | image_name = os.path.join(img_path0, name.split('.')[0] + '.jpg') 65 | 66 | if os.path.exists(image_name): 67 | # 打开xml文档 68 | tree = ET.parse(os.path.join(xml_path0, name)) 69 | img = cv2.imread(image_name) 70 | box_thickness = int((img.shape[0] + img.shape[1])/1000) + 1 # 标注框的一个参数。本人图像大小不一致,在不同大小的图像上展示不同粗细的bbox 71 | 72 | text_thickness = box_thickness 73 | text_size = float(text_thickness/2) # 显示标注类别的参数。字体大小。这些不是重点。不想要可以删掉。 74 | font = cv2.FONT_HERSHEY_SIMPLEX 75 | 76 | # 得到文档元素对象 77 | root = tree.getroot() 78 | allObjects = root.findall('object') 79 | if len(allObjects) == 0: 80 | print("1 :", name) 81 | _image_name = str(image_name) 82 | _xml_name = _image_name.replace('.jpg', '.xml') 83 | _xml_name = _xml_name.replace('train', 'train_xml') 84 | if os.path.exists(_image_name): 85 | os.remove(_image_name) 86 | else: 87 | print("=========", _image_name) 88 | if os.path.exists(_xml_name): 89 | os.remove(_xml_name) 90 | else: 91 | print("=========", _xml_name) 92 | continue 93 | 94 | for i in range(len(allObjects)): # 遍历xml标签,画框并显示类别。 95 | object = allObjects[i] 96 | objectName = object.find('name').text 97 | 98 | xmin = int(object.find('bndbox').find('xmin').text) 99 | ymin = int(object.find('bndbox').find('ymin').text) 100 | xmax = int(object.find('bndbox').find('xmax').text) 101 | ymax = int(object.find('bndbox').find('ymax').text) 102 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (0,0,0), text_thickness) 103 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 104 | 105 | if len(allObjects) == 0: 106 | print("error") 107 | 108 | name = name.replace('xml', 'jpg') 109 | img_save_path = os.path.join(img_xml, name) 110 | cv2.imwrite(img_save_path, img) 111 | ''' -------------------------------------------------------------------------------- /tools/process_labels/noise_position.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | 5 | def position_ratio(rate): 6 | x_rate = random.randint(-rate, rate)/100 7 | y_rate = random.randint(-rate, rate)/100 8 | return x_rate, y_rate 9 | 10 | 11 | def norm(noise): 12 | if noise<0: 13 | noise = 0.00000001 14 | print("norm") 15 | if noise>1: 16 | noise = 0.99999999 17 | print("norm") 18 | 19 | return noise 20 | 21 | 22 | def add_position_noise(info, Rate): 23 | x = float(info[1]) 24 | y = float(info[2]) 25 | w = float(info[3]) 26 | h = float(info[4]) 27 | x_rate, y_rate = position_ratio(rate=Rate) 28 | 29 | x_noise = x + w * x_rate 30 | y_noise = y + h * y_rate 31 | 32 | if (x_noise < w/2) or ((1-x_noise) < w/2): 33 | x_noise = x 34 | print("x") 35 | if (y_noise < h/2) or ((1-y_noise) < h/2): 36 | y_noise = y 37 | print("y") 38 | 39 | x_noise = str(norm(x_noise)) 40 | y_noise = str(norm(y_noise)) 41 | 42 | info[1] = x_noise 43 | info[2] = y_noise 44 | 45 | return info 46 | 47 | 48 | def noise_txt(txt_file, target, rate): 49 | target_file = txt_file.replace('labels_ori_txt', target) 50 | with open(target_file, 'w+') as saver: 51 | with open(txt_file, 'r+') as file: 52 | lines = file.readlines() 53 | file.seek(0, 0) #set the pointer to 0,0 cordinate of file 54 | for line in lines: 55 | row = line.strip().split(" ") 56 | if len(row) == 5: 57 | position_noise = add_position_noise(row, rate) 58 | saver.write(" ".join(position_noise) + "\n") 59 | else: 60 | print(row) 61 | saver.close() 62 | 63 | def run(txt_path='/Volumes/Data/nosie_labels/', target = None, Rate=5): 64 | for roots, dirs, files in os.walk(txt_path): 65 | print(roots) 66 | target_dir = roots.replace('labels_ori_txt', target) 67 | if not os.path.exists(target_dir): 68 | os.mkdir(target_dir) 69 | for file in files: 70 | if file.endswith('.txt'): 71 | file_path = os.path.join(roots, file) 72 | noise_txt(txt_file=file_path, target = target, rate=Rate) 73 | 74 | print("Noise rate {} Done!".format(Rate)) 75 | 76 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori/', target='labels_position_noise5',Rate=5) 77 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori/', target='labels_position_noise10',Rate=10) 78 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori/', target='labels_position_noise15',Rate=15) 79 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori/', target='labels_position_noise20',Rate=20) 80 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_position_noise25',Rate=25) 81 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_position_noise30',Rate=30) 82 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_position_noise35',Rate=35) 83 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_position_noise40',Rate=40) 84 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_position_noise45',Rate=45) 85 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_position_noise50',Rate=50) 86 | 87 | 88 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise5',Rate=5) 89 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise10',Rate=10) 90 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise15',Rate=15) 91 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise20',Rate=20) 92 | 93 | 94 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise25',Rate=25) 95 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise30',Rate=30) 96 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise35',Rate=35) 97 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise40',Rate=40) 98 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise45',Rate=45) 99 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_position_noise50',Rate=50) -------------------------------------------------------------------------------- /tools/process_labels/noise_size.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | 5 | def concat_ratio(rate): 6 | x_rate = random.randint(-rate, rate)/100 7 | y_rate = random.randint(-rate, rate)/100 8 | w_rate = random.randint(-rate, rate)/100 9 | h_rate = random.randint(-rate, rate)/100 10 | return x_rate, y_rate, w_rate, h_rate 11 | 12 | 13 | def norm(noise): 14 | if noise<0: 15 | noise = 0.00000001 16 | print("norm") 17 | if noise>1: 18 | noise = 0.99999999 19 | print("norm") 20 | 21 | return noise 22 | 23 | 24 | def add_size_noise(info, Rate): 25 | x = float(info[1]) 26 | y = float(info[2]) 27 | w = float(info[3]) 28 | h = float(info[4]) 29 | x_rate, y_rate, w_rate, h_rate = concat_ratio(rate=Rate) 30 | 31 | w_noise = w + w * w_rate 32 | h_noise = h + h * h_rate 33 | 34 | if (x < w_noise/2) or ((1-x) < w_noise/2): 35 | w_noise = w 36 | print("x") 37 | if (y < h_noise/2) or ((1-y) < h_noise/2): 38 | h_noise = h 39 | print("y") 40 | 41 | w_noise = str(norm(w_noise)) 42 | h_noise = str(norm(h_noise)) 43 | 44 | info[3] = w_noise 45 | info[4] = h_noise 46 | 47 | return info 48 | 49 | 50 | def noise_txt(txt_file, target, rate): 51 | target_file = txt_file.replace('labels_ori_txt', target) 52 | with open(target_file, 'w+') as saver: 53 | with open(txt_file, 'r+') as file: 54 | lines = file.readlines() 55 | file.seek(0, 0) #set the pointer to 0,0 cordinate of file 56 | for line in lines: 57 | row = line.strip().split(" ") 58 | if len(row) == 5: 59 | position_noise = add_size_noise(row, rate) 60 | saver.write(" ".join(position_noise) + "\n") 61 | else: 62 | print(row) 63 | saver.close() 64 | 65 | def run(txt_path='/Volumes/Data/nosie_labels/', target = None, Rate=5): 66 | for roots, dirs, files in os.walk(txt_path): 67 | print(roots) 68 | target_dir = roots.replace('labels_ori_txt', target) 69 | if not os.path.exists(target_dir): 70 | os.mkdir(target_dir) 71 | for file in files: 72 | if file.endswith('.txt'): 73 | file_path = os.path.join(roots, file) 74 | noise_txt(txt_file=file_path, target = target, rate=Rate) 75 | 76 | print("Noise rate {} Done!".format(Rate)) 77 | 78 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise5',Rate=5) 79 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise10',Rate=10) 80 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise15',Rate=15) 81 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise20',Rate=20) 82 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise25',Rate=25) 83 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise30',Rate=30) 84 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise35',Rate=35) 85 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise40',Rate=40) 86 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise45',Rate=45) 87 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_size_noise50',Rate=50) 88 | 89 | 90 | 91 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise5',Rate=5) 92 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise10',Rate=10) 93 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise15',Rate=15) 94 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise20',Rate=20) 95 | 96 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise25',Rate=25) 97 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise30',Rate=30) 98 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise35',Rate=35) 99 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise40',Rate=40) 100 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise45',Rate=45) 101 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_size_noise50',Rate=50) 102 | -------------------------------------------------------------------------------- /tools/process_labels/calculate_bounding_box.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | import cv2 3 | import os 4 | from tqdm import tqdm 5 | 6 | blossom_end_rot = 0 7 | graymold = 0 8 | powdery_mildew = 0 9 | spider_mite = 0 10 | spotting_disease = 0 11 | 12 | 13 | folder = 'dataset_test' 14 | xml_path = '/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels/train_xml' # 你的xml文件路径 15 | img_path = '/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/images/train' # 图像路径 16 | 17 | for name in tqdm(os.listdir(xml_path)): 18 | image_name = os.path.join(img_path, name.split('.')[0] + '.jpg') 19 | 20 | if os.path.exists(image_name): 21 | # 打开xml文档 22 | tree = ET.parse(os.path.join(xml_path,name)) 23 | img = cv2.imread(image_name) 24 | box_thickness = int((img.shape[0] + img.shape[1])/1000) + 1 # 标注框的一个参数。本人图像大小不一致,在不同大小的图像上展示不同粗细的bbox 25 | 26 | text_thickness = box_thickness 27 | text_size = float(text_thickness/2) # 显示标注类别的参数。字体大小。这些不是重点。不想要可以删掉。 28 | font = cv2.FONT_HERSHEY_SIMPLEX 29 | 30 | # 得到文档元素对象 31 | root = tree.getroot() 32 | allObjects = root.findall('object') 33 | if len(allObjects) == 0: 34 | print("1 :", name) 35 | _image_name = str(image_name) 36 | _xml_name = _image_name.replace('.jpg', '.xml') 37 | _xml_name = _xml_name.replace('train', 'train_xml') 38 | if os.path.exists(_image_name): 39 | os.remove(_image_name) 40 | else: 41 | print("=========", _image_name) 42 | if os.path.exists(_xml_name): 43 | os.remove(_xml_name) 44 | else: 45 | print("=========", _xml_name) 46 | continue 47 | 48 | for i in range(len(allObjects)): # 遍历xml标签,画框并显示类别。 49 | object = allObjects[i] 50 | objectName = object.find('name').text 51 | 52 | if objectName == 'blossom_end_rot': # 把引号里的内容更改为自己的类别即可。 53 | xmin = int(object.find('bndbox').find('xmin').text) 54 | ymin = int(object.find('bndbox').find('ymin').text) 55 | xmax = int(object.find('bndbox').find('xmax').text) 56 | ymax = int(object.find('bndbox').find('ymax').text) 57 | #cv2.putText(img, objectName, (xmin, ymax), font, text_size, 58 | # (255,255,255), text_thickness) 59 | #cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 60 | blossom_end_rot+=1 61 | 62 | if objectName == 'graymold': 63 | xmin = int(object.find('bndbox').find('xmin').text) 64 | ymin = int(object.find('bndbox').find('ymin').text) 65 | xmax = int(object.find('bndbox').find('xmax').text) 66 | ymax = int(object.find('bndbox').find('ymax').text) 67 | #cv2.putText(img, objectName, (xmin, ymax), font, text_size, 68 | # (255,255,255), text_thickness) 69 | #cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 70 | graymold+=1 71 | 72 | if objectName == 'powdery_mildew': 73 | xmin = int(object.find('bndbox').find('xmin').text) 74 | ymin = int(object.find('bndbox').find('ymin').text) 75 | xmax = int(object.find('bndbox').find('xmax').text) 76 | ymax = int(object.find('bndbox').find('ymax').text) 77 | #cv2.putText(img, objectName, (xmin, ymax), font, text_size, 78 | # (255,255,255), text_thickness) 79 | #cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 80 | powdery_mildew+=1 81 | 82 | if objectName == 'spider_mite': 83 | xmin = int(object.find('bndbox').find('xmin').text) 84 | ymin = int(object.find('bndbox').find('ymin').text) 85 | xmax = int(object.find('bndbox').find('xmax').text) 86 | ymax = int(object.find('bndbox').find('ymax').text) 87 | #cv2.putText(img, objectName, (xmin, ymax), font, text_size, 88 | # (255,255,255), text_thickness) 89 | #cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 90 | spider_mite+=1 91 | 92 | if objectName == 'spotting_disease': 93 | xmin = int(object.find('bndbox').find('xmin').text) 94 | ymin = int(object.find('bndbox').find('ymin').text) 95 | xmax = int(object.find('bndbox').find('xmax').text) 96 | ymax = int(object.find('bndbox').find('ymax').text) 97 | #cv2.putText(img, objectName, (xmin, ymax), font, text_size, 98 | # (255,255,255), text_thickness) 99 | #cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 100 | spotting_disease+=1 101 | 102 | if objectName not in ['blossom_end_rot', 'graymold', 'powdery_mildew', 'spider_mite', 'spotting_disease']: 103 | xmin = int(object.find('bndbox').find('xmin').text) 104 | ymin = int(object.find('bndbox').find('ymin').text) 105 | xmax = int(object.find('bndbox').find('xmax').text) 106 | ymax = int(object.find('bndbox').find('ymax').text) 107 | #cv2.putText(img, objectName, (xmin, ymax), font, text_size, 108 | # (255, 255, 255), text_thickness) 109 | #cv2.rectangle(img, (xmin, ymin), (xmax, ymax), [0, 0, 0], box_thickness) 110 | print('objectName not in these labels. It is :', objectName) 111 | if len(allObjects) == 0: 112 | print("error") 113 | 114 | #name = name.replace('xml', 'jpg') 115 | #img_save_path = os.path.join(img_xml, name) 116 | #cv2.imwrite(img_save_path, img) 117 | 118 | print("blossom_end_rot boxs is :", blossom_end_rot) 119 | print("graymold is :", graymold) 120 | print("powdery_mildew is :", powdery_mildew) 121 | print("spider_mite is :", spider_mite) 122 | print("spotting_disease boxs is :", spotting_disease) 123 | -------------------------------------------------------------------------------- /tools/process_labels/noise_concat_using_xmlfile.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | import random 3 | import os 4 | 5 | def noise_ratio(rate): 6 | a = random.randint(0, 99) 7 | noise = False 8 | if a < rate: 9 | noise = True 10 | return noise 11 | 12 | 13 | def get_value(category, xmin, xmax, ymin, ymax): 14 | category = str(category.text) 15 | xmin = int(xmin.text) 16 | xmax = int(xmax.text) 17 | ymin = int(ymin.text) 18 | ymax = int(ymax.text) 19 | 20 | return category, xmin, xmax, ymin, ymax 21 | 22 | "blossom_end_rot", "graymold", "powdery_mildew", "spider_mite","spotting_disease" 23 | def add_category_noise(org_label): 24 | if org_label == "blossom_end_rot": 25 | new_label = random.choice(["graymold", "powdery_mildew", "spider_mite", "spotting_disease"]) 26 | elif org_label == "graymold": 27 | new_label = random.choice(["blossom_end_rot", "powdery_mildew", "spider_mite", "spotting_disease"]) 28 | elif org_label == "powdery_mildew": 29 | new_label = random.choice(["blossom_end_rot", "graymold", "spider_mite", "spotting_disease"]) 30 | elif org_label == "spider_mite": 31 | new_label = random.choice(["blossom_end_rot", "graymold", "powdery_mildew","spotting_disease"]) 32 | elif org_label == "spotting_disease": 33 | new_label = random.choice(["blossom_end_rot", "graymold", "powdery_mildew", "spider_mite"]) 34 | else: 35 | print("Wrong") 36 | return new_label 37 | 38 | 39 | def concat_ratio(rate): 40 | x_rate = random.randint(-rate, rate)/100 41 | y_rate = random.randint(-rate, rate)/100 42 | w_rate = random.randint(-rate, rate)/100 43 | h_rate = random.randint(-rate, rate)/100 44 | return x_rate, y_rate, w_rate, h_rate 45 | 46 | 47 | def add_concat_noise(old_xmin, old_xmax, old_ymin, old_ymax, rate): 48 | x = (old_xmin + old_xmax) / 2 49 | y = (old_ymin + old_ymax) / 2 50 | w = old_xmax - old_xmin 51 | h = old_ymax - old_ymin 52 | x_rate, y_rate, w_rate, h_rate = concat_ratio(rate=rate) 53 | x_noise = x + w * x_rate 54 | y_noise = y + h * y_rate 55 | w_noise = w + w * w_rate 56 | h_noise = h + h * h_rate 57 | 58 | new_xmin = int(x_noise - (w_noise/2)) 59 | new_xmax = int(x_noise + (w_noise/2)) 60 | new_ymin = int(y_noise - (h_noise/2)) 61 | new_ymax = int(y_noise + (h_noise/2)) 62 | 63 | return new_xmin, new_xmax, new_ymin, new_ymax 64 | 65 | 66 | def norm(new_xmin, new_xmax, new_ymin, new_ymax, width, height): 67 | 68 | if new_xmin < 0: 69 | new_xmin = 1 70 | if new_xmax >= width: 71 | new_xmax = width - 1 72 | if new_ymin < 0: 73 | new_ymin = 1 74 | if new_ymax >= height: 75 | new_ymax = height - 1 76 | 77 | return new_xmin, new_xmax, new_ymin, new_ymax 78 | 79 | 80 | def noise_xml(xml_file, target, rate): 81 | target_file = xml_file.replace('labels', target) 82 | 83 | tree = ET.parse(xml_file) 84 | root = tree.getroot() 85 | 86 | size = root.find('size') 87 | width = int(size.find('width').text) 88 | height = int(size.find('height').text) 89 | 90 | 91 | for object in root.findall('object'): 92 | category = object.find('name') 93 | bndbox = object.find('bndbox') # 子节点下节点rank的值 94 | xmin = bndbox.find('xmin') # type(xmin) = int 95 | xmax = bndbox.find('xmax') 96 | ymin = bndbox.find('ymin') 97 | ymax = bndbox.find('ymax') 98 | 99 | old_category, old_xmin, old_xmax, old_ymin, old_ymax = get_value(category, xmin, xmax, ymin, ymax) 100 | new_xmin, new_xmax, new_ymin, new_ymax = add_concat_noise(old_xmin, old_xmax, old_ymin, old_ymax, rate=rate) 101 | new_xmin, new_xmax, new_ymin, new_ymax = norm(new_xmin, new_xmax, new_ymin, new_ymax, width, height) 102 | noise_class = noise_ratio(rate=rate) 103 | if noise_class: 104 | new_cotegory = add_category_noise(old_category) 105 | category.text = str(new_cotegory) 106 | xmin.text = str(new_xmin) 107 | xmax.text = str(new_xmax) 108 | ymin.text = str(new_ymin) 109 | ymax.text = str(new_ymax) 110 | 111 | tree.write(target_file) 112 | 113 | def run(xml_path= None, target = None, Rate = None): 114 | for roots, dirs, files in os.walk(xml_path): 115 | print(roots) 116 | target_dir = roots.replace('labels', target) 117 | if not os.path.exists(target_dir): 118 | os.mkdir(target_dir) 119 | for file in files: 120 | if file.endswith('.xml'): 121 | file_path = os.path.join(roots, file) 122 | noise_xml(xml_file=file_path, target = target, rate=Rate) 123 | 124 | print("Noise rate {} Done!".format(Rate)) 125 | 126 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori/', target='labels_concat_noise5',Rate=5) 127 | # run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise5', Rate=5) 128 | # run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise10', Rate=10) 129 | # run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise15', Rate=15) 130 | # run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise20', Rate=20) 131 | run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise25', Rate=25) 132 | run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise30', Rate=30) 133 | run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise35', Rate=35) 134 | run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise40', Rate=40) 135 | run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise45', Rate=45) 136 | run(xml_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_xmlnoise/labels/', target='labels_concat_noise50', Rate=50) 137 | -------------------------------------------------------------------------------- /tools/show_xml/show_xml_on_paprika.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | import cv2 3 | import os 4 | from tqdm import tqdm 5 | 6 | blossom_end_rot = 0 7 | graymold = 0 8 | powdery_mildew = 0 9 | spider_mite = 0 10 | spotting_disease = 0 11 | 12 | 13 | folder = 'dataset_test' 14 | xml_path = '/Users/Dong/Documents/test/test_xml' # 你的xml文件路径 15 | img_path = '/Users/Dong/Documents/test/detect' # 图像路径 16 | 17 | if 'wrong' in img_path: 18 | img_xml = '/Users/Dong/Documents/test/show_wrong' # 显示标注框保存该文件的路径 19 | else: 20 | img_xml = '/Users/Dong/Documents/test/show_right' # 显示标注框保存该文件的路径 21 | 22 | 23 | if not os.path.exists(img_xml): 24 | os.mkdir(img_xml) 25 | 26 | for name in tqdm(os.listdir(xml_path)): 27 | image_name = os.path.join(img_path, name.split('.')[0] + '.jpg') 28 | 29 | if os.path.exists(image_name): 30 | # 打开xml文档 31 | tree = ET.parse(os.path.join(xml_path,name)) 32 | img = cv2.imread(image_name) 33 | box_thickness = int((img.shape[0] + img.shape[1])/1000) + 1 # 标注框的一个参数。本人图像大小不一致,在不同大小的图像上展示不同粗细的bbox 34 | 35 | text_thickness = box_thickness 36 | text_size = float(text_thickness/2) # 显示标注类别的参数。字体大小。这些不是重点。不想要可以删掉。 37 | font = cv2.FONT_HERSHEY_SIMPLEX 38 | 39 | # 得到文档元素对象 40 | root = tree.getroot() 41 | allObjects = root.findall('object') 42 | if len(allObjects) == 0: 43 | print("1 :", name) 44 | _image_name = str(image_name) 45 | _xml_name = _image_name.replace('.jpg', '.xml') 46 | _xml_name = _xml_name.replace('train', 'train_xml') 47 | if os.path.exists(_image_name): 48 | os.remove(_image_name) 49 | else: 50 | print("=========", _image_name) 51 | if os.path.exists(_xml_name): 52 | os.remove(_xml_name) 53 | else: 54 | print("=========", _xml_name) 55 | continue 56 | 57 | for i in range(len(allObjects)): # 遍历xml标签,画框并显示类别。 58 | object = allObjects[i] 59 | objectName = object.find('name').text 60 | 61 | if objectName == 'blossom_end_rot': # 把引号里的内容更改为自己的类别即可。 62 | xmin = int(object.find('bndbox').find('xmin').text) 63 | ymin = int(object.find('bndbox').find('ymin').text) 64 | xmax = int(object.find('bndbox').find('xmax').text) 65 | ymax = int(object.find('bndbox').find('ymax').text) 66 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, 67 | (255,255,255), text_thickness) 68 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 69 | blossom_end_rot+=1 70 | 71 | if objectName == 'graymold': 72 | xmin = int(object.find('bndbox').find('xmin').text) 73 | ymin = int(object.find('bndbox').find('ymin').text) 74 | xmax = int(object.find('bndbox').find('xmax').text) 75 | ymax = int(object.find('bndbox').find('ymax').text) 76 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, 77 | (255,255,255), text_thickness) 78 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 79 | graymold+=1 80 | 81 | if objectName == 'powdery_mildew': 82 | xmin = int(object.find('bndbox').find('xmin').text) 83 | ymin = int(object.find('bndbox').find('ymin').text) 84 | xmax = int(object.find('bndbox').find('xmax').text) 85 | ymax = int(object.find('bndbox').find('ymax').text) 86 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, 87 | (255,255,255), text_thickness) 88 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 89 | powdery_mildew+=1 90 | 91 | if objectName == 'spider_mite': 92 | xmin = int(object.find('bndbox').find('xmin').text) 93 | ymin = int(object.find('bndbox').find('ymin').text) 94 | xmax = int(object.find('bndbox').find('xmax').text) 95 | ymax = int(object.find('bndbox').find('ymax').text) 96 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, 97 | (255,255,255), text_thickness) 98 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 99 | spider_mite+=1 100 | 101 | if objectName == 'spotting_disease': 102 | xmin = int(object.find('bndbox').find('xmin').text) 103 | ymin = int(object.find('bndbox').find('ymin').text) 104 | xmax = int(object.find('bndbox').find('xmax').text) 105 | ymax = int(object.find('bndbox').find('ymax').text) 106 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, 107 | (255,255,255), text_thickness) 108 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),[255,255,255],box_thickness) 109 | spotting_disease+=1 110 | 111 | if objectName not in ['blossom_end_rot', 'graymold', 'powdery_mildew', 'spider_mite', 'spotting_disease']: 112 | xmin = int(object.find('bndbox').find('xmin').text) 113 | ymin = int(object.find('bndbox').find('ymin').text) 114 | xmax = int(object.find('bndbox').find('xmax').text) 115 | ymax = int(object.find('bndbox').find('ymax').text) 116 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, 117 | (255, 255, 255), text_thickness) 118 | cv2.rectangle(img, (xmin, ymin), (xmax, ymax), [0, 0, 0], box_thickness) 119 | print('objectName not in these labels. It is :', objectName) 120 | if len(allObjects) == 0: 121 | print("error") 122 | 123 | name = name.replace('xml', 'jpg') 124 | img_save_path = os.path.join(img_xml, name) 125 | cv2.imwrite(img_save_path, img) 126 | 127 | print("blossom_end_rot boxs is :", blossom_end_rot) 128 | print("graymold is :", graymold) 129 | print("powdery_mildew is :", powdery_mildew) 130 | print("spider_mite is :", spider_mite) 131 | print("spotting_disease boxs is :", spotting_disease) 132 | -------------------------------------------------------------------------------- /tools/process_labels/noise_concat.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | 5 | def noise_ratio(rate): 6 | a = random.randint(0, 99) 7 | noise = False 8 | if a < rate: 9 | noise = True 10 | return noise 11 | 12 | 13 | def noise_label(org_label): 14 | if org_label == '0': 15 | new_label = random.choice('1234') 16 | elif org_label == '1': 17 | new_label = random.choice('0234') 18 | elif org_label == '2': 19 | new_label = random.choice('0134') 20 | elif org_label == '3': 21 | new_label = random.choice('0124') 22 | else: 23 | new_label = random.choice('0123') 24 | return new_label 25 | 26 | 27 | def concat_ratio(rate): 28 | x_rate = random.randint(-rate, rate)/100 29 | y_rate = random.randint(-rate, rate)/100 30 | w_rate = random.randint(-rate, rate)/100 31 | h_rate = random.randint(-rate, rate)/100 32 | return x_rate, y_rate, w_rate, h_rate 33 | 34 | 35 | def norm(noise): 36 | if noise<0: 37 | noise = 0.00000001 38 | print("norm") 39 | if noise>1: 40 | noise = 0.99999999 41 | print("norm") 42 | 43 | return noise 44 | 45 | def add_concat_noise(info, Rate): 46 | x = float(info[1]) 47 | y = float(info[2]) 48 | w = float(info[3]) 49 | h = float(info[4]) 50 | x_rate, y_rate, w_rate, h_rate = concat_ratio(rate=Rate) 51 | x_noise = x + w * x_rate 52 | y_noise = y + h * y_rate 53 | w_noise = w + w * w_rate 54 | h_noise = h + h * h_rate 55 | 56 | if (x_noise < w_noise/2) or ((1-x_noise) < w_noise/2): 57 | x_noise = x 58 | w_noise = w 59 | print("x") 60 | if (y_noise < h_noise/2) or ((1-y_noise) < h_noise/2): 61 | y_noise = y 62 | h_noise = h 63 | print("y") 64 | 65 | x_noise = str(norm(x_noise)) 66 | y_noise = str(norm(y_noise)) 67 | w_noise = str(norm(w_noise)) 68 | h_noise = str(norm(h_noise)) 69 | info[1] = x_noise 70 | info[2] = y_noise 71 | info[3] = w_noise 72 | info[4] = h_noise 73 | 74 | return info 75 | 76 | 77 | def noise_txt(txt_file, target, rate): 78 | target_file = txt_file.replace('labels_ori_txt', target) 79 | 80 | with open(target_file, 'w+') as saver: 81 | with open(txt_file, 'r+') as file: 82 | lines = file.readlines() 83 | file.seek(0, 0) #set the pointer to 0,0 cordinate of file 84 | for line in lines: 85 | row = line.strip().split(" ") 86 | if len(row) == 5: 87 | position_noise = add_concat_noise(row, rate) 88 | noise_class = noise_ratio(rate) 89 | if noise_class: 90 | wrong_label = noise_label(position_noise[0]) 91 | #print("The label is changed from {} to {}.".format(position_noise[0], wrong_label)) 92 | position_noise[0] = wrong_label 93 | saver.write(" ".join(position_noise)+"\n") 94 | else: 95 | print(row) 96 | saver.close() 97 | 98 | 99 | def run(txt_path='/Volumes/Data/nosie_labels/', target = None, Rate=5): 100 | for roots, dirs, files in os.walk(txt_path): 101 | print(roots) 102 | target_dir = roots.replace('labels_ori_txt', target) 103 | if not os.path.exists(target_dir): 104 | os.mkdir(target_dir) 105 | for file in files: 106 | if file.endswith('.txt'): 107 | file_path = os.path.join(roots, file) 108 | noise_txt(txt_file=file_path, target = target, rate=Rate) 109 | 110 | print("Noise rate {} Done!".format(Rate)) 111 | 112 | 113 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise5',Rate=5) 114 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise10',Rate=10) 115 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise15',Rate=15) 116 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise20',Rate=20) 117 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise25',Rate=25) 118 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise30',Rate=30) 119 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise35',Rate=35) 120 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise40',Rate=40) 121 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise45',Rate=45) 122 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4_aug/labels_ori_txt/', target='labels_concat_noise50',Rate=50) 123 | 124 | 125 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise5',Rate=5) 126 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise10',Rate=10) 127 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise15',Rate=15) 128 | #run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise20',Rate=20) 129 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise25',Rate=25) 130 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise30',Rate=30) 131 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise35',Rate=35) 132 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise40',Rate=40) 133 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise45',Rate=45) 134 | run(txt_path='/Volumes/Data/project_paper_code_data/yolov5-official_new/datasets/paprika_v4/labels_ori_txt/', target='labels_concat_noise50',Rate=50) -------------------------------------------------------------------------------- /hubconf.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | PyTorch Hub models https://pytorch.org/hub/ultralytics_yolov5/ 4 | 5 | Usage: 6 | import torch 7 | model = torch.hub.load('ultralytics/yolov5', 'yolov5s') 8 | model = torch.hub.load('ultralytics/yolov5:master', 'custom', 'path/to/yolov5s.onnx') # file from branch 9 | """ 10 | 11 | import torch 12 | 13 | 14 | def _create(name, pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 15 | """Creates or loads a YOLOv5 model 16 | 17 | Arguments: 18 | name (str): model name 'yolov5s' or path 'path/to/best.pt' 19 | pretrained (bool): load pretrained weights into the model 20 | channels (int): number of input channels 21 | classes (int): number of model classes 22 | autoshape (bool): apply YOLOv5 .autoshape() wrapper to model 23 | verbose (bool): print all information to screen 24 | device (str, torch.device, None): device to use for model parameters 25 | 26 | Returns: 27 | YOLOv5 model 28 | """ 29 | from pathlib import Path 30 | 31 | from models.common import AutoShape, DetectMultiBackend 32 | from models.yolo import Model 33 | from utils.downloads import attempt_download 34 | from utils.general import LOGGER, check_requirements, intersect_dicts, logging 35 | from utils.torch_utils import select_device 36 | 37 | if not verbose: 38 | LOGGER.setLevel(logging.WARNING) 39 | check_requirements(exclude=('tensorboard', 'thop', 'opencv-python')) 40 | name = Path(name) 41 | path = name.with_suffix('.pt') if name.suffix == '' else name # checkpoint path 42 | try: 43 | device = select_device(('0' if torch.cuda.is_available() else 'cpu') if device is None else device) 44 | 45 | if pretrained and channels == 3 and classes == 80: 46 | model = DetectMultiBackend(path, device=device) # download/load FP32 model 47 | # model = models.experimental.attempt_load(path, map_location=device) # download/load FP32 model 48 | else: 49 | cfg = list((Path(__file__).parent / 'models').rglob(f'{path.stem}.yaml'))[0] # model.yaml path 50 | model = Model(cfg, channels, classes) # create model 51 | if pretrained: 52 | ckpt = torch.load(attempt_download(path), map_location=device) # load 53 | csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32 54 | csd = intersect_dicts(csd, model.state_dict(), exclude=['anchors']) # intersect 55 | model.load_state_dict(csd, strict=False) # load 56 | if len(ckpt['model'].names) == classes: 57 | model.names = ckpt['model'].names # set class names attribute 58 | if autoshape: 59 | model = AutoShape(model) # for file/URI/PIL/cv2/np inputs and NMS 60 | return model.to(device) 61 | 62 | except Exception as e: 63 | help_url = 'https://github.com/ultralytics/yolov5/issues/36' 64 | s = f'{e}. Cache may be out of date, try `force_reload=True` or see {help_url} for help.' 65 | raise Exception(s) from e 66 | 67 | 68 | def custom(path='path/to/model.pt', autoshape=True, verbose=True, device=None): 69 | # YOLOv5 custom or local model 70 | return _create(path, autoshape=autoshape, verbose=verbose, device=device) 71 | 72 | 73 | def yolov5n(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 74 | # YOLOv5-nano model https://github.com/ultralytics/yolov5 75 | return _create('yolov5n', pretrained, channels, classes, autoshape, verbose, device) 76 | 77 | 78 | def yolov5s(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 79 | # YOLOv5-small model https://github.com/ultralytics/yolov5 80 | return _create('yolov5s', pretrained, channels, classes, autoshape, verbose, device) 81 | 82 | 83 | def yolov5m(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 84 | # YOLOv5-medium model https://github.com/ultralytics/yolov5 85 | return _create('yolov5m', pretrained, channels, classes, autoshape, verbose, device) 86 | 87 | 88 | def yolov5l(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 89 | # YOLOv5-large model https://github.com/ultralytics/yolov5 90 | return _create('yolov5l', pretrained, channels, classes, autoshape, verbose, device) 91 | 92 | 93 | def yolov5x(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 94 | # YOLOv5-xlarge model https://github.com/ultralytics/yolov5 95 | return _create('yolov5x', pretrained, channels, classes, autoshape, verbose, device) 96 | 97 | 98 | def yolov5n6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 99 | # YOLOv5-nano-P6 model https://github.com/ultralytics/yolov5 100 | return _create('yolov5n6', pretrained, channels, classes, autoshape, verbose, device) 101 | 102 | 103 | def yolov5s6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 104 | # YOLOv5-small-P6 model https://github.com/ultralytics/yolov5 105 | return _create('yolov5s6', pretrained, channels, classes, autoshape, verbose, device) 106 | 107 | 108 | def yolov5m6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 109 | # YOLOv5-medium-P6 model https://github.com/ultralytics/yolov5 110 | return _create('yolov5m6', pretrained, channels, classes, autoshape, verbose, device) 111 | 112 | 113 | def yolov5l6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 114 | # YOLOv5-large-P6 model https://github.com/ultralytics/yolov5 115 | return _create('yolov5l6', pretrained, channels, classes, autoshape, verbose, device) 116 | 117 | 118 | def yolov5x6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 119 | # YOLOv5-xlarge-P6 model https://github.com/ultralytics/yolov5 120 | return _create('yolov5x6', pretrained, channels, classes, autoshape, verbose, device) 121 | 122 | 123 | if __name__ == '__main__': 124 | model = _create(name='yolov5s', pretrained=True, channels=3, classes=80, autoshape=True, verbose=True) # pretrained 125 | # model = custom(path='path/to/model.pt') # custom 126 | 127 | # Verify inference 128 | from pathlib import Path 129 | 130 | import cv2 131 | import numpy as np 132 | from PIL import Image 133 | 134 | imgs = ['data/images/zidane.jpg', # filename 135 | Path('data/images/zidane.jpg'), # Path 136 | 'https://ultralytics.com/images/zidane.jpg', # URI 137 | cv2.imread('data/images/bus.jpg')[:, :, ::-1], # OpenCV 138 | Image.open('data/images/bus.jpg'), # PIL 139 | np.zeros((320, 640, 3))] # numpy 140 | 141 | results = model(imgs, size=320) # batched inference 142 | results.print() 143 | results.save() 144 | -------------------------------------------------------------------------------- /tools/show_xml/show_xml_on_resultimage.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | import cv2 3 | import os 4 | from tqdm import tqdm 5 | 6 | anthracnose_runner = 0 7 | gray_mold = 0 8 | blossom_blight = 0 9 | leaf_spot = 0 10 | powdery_mildew_fruit = 0 11 | powdery_mildew_leaf = 0 12 | anthracnose_fruit_rot = 0 13 | angular_leafspot = 0 14 | 15 | color = [255,255,255] 16 | 17 | folder = 'dataset_test' 18 | xml_path = '/Users/Dong/Desktop/test/' + folder + '/test_xml' # 你的xml文件路径 19 | img_path = '/Users/Dong/Desktop/test/' + folder + '/detect' # 图像路径 20 | img_xml = '/Users/Dong/Desktop/test/' + folder + '/show_test' # 显示标注框保存该文件的路径 21 | for name in tqdm(os.listdir(xml_path)): 22 | image_name = os.path.join(img_path, name.split('.')[0] + '.jpg') 23 | 24 | if os.path.exists(image_name): 25 | # 打开xml文档 26 | tree = ET.parse(os.path.join(xml_path,name)) 27 | img = cv2.imread(image_name) 28 | box_thickness = int((img.shape[0] + img.shape[1])/1000) + 1 # 标注框的一个参数。本人图像大小不一致,在不同大小的图像上展示不同粗细的bbox 29 | 30 | text_thickness = box_thickness 31 | text_size = float(text_thickness/2) # 显示标注类别的参数。字体大小。这些不是重点。不想要可以删掉。 32 | font = cv2.FONT_HERSHEY_SIMPLEX 33 | 34 | # 得到文档元素对象 35 | root = tree.getroot() 36 | allObjects = root.findall('object') 37 | if len(allObjects) == 0: 38 | print("1 :", name) 39 | _image_name = str(image_name) 40 | _xml_name = _image_name.replace('.jpg', '.xml') 41 | _xml_name = _xml_name.replace('train', 'train_xml') 42 | if os.path.exists(_image_name): 43 | os.remove(_image_name) 44 | else: 45 | print("=========", _image_name) 46 | if os.path.exists(_xml_name): 47 | os.remove(_xml_name) 48 | else: 49 | print("=========", _xml_name) 50 | continue 51 | 52 | for i in range(len(allObjects)): # 遍历xml标签,画框并显示类别。 53 | object = allObjects[i] 54 | objectName = object.find('name').text 55 | 56 | if objectName == 'angular_leafspot': 57 | xmin = int(object.find('bndbox').find('xmin').text) 58 | ymin = int(object.find('bndbox').find('ymin').text) 59 | xmax = int(object.find('bndbox').find('xmax').text) 60 | ymax = int(object.find('bndbox').find('ymax').text) 61 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 62 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 63 | angular_leafspot+=1 64 | 65 | if objectName == 'anthracnose_fruit_rot': 66 | xmin = int(object.find('bndbox').find('xmin').text) 67 | ymin = int(object.find('bndbox').find('ymin').text) 68 | xmax = int(object.find('bndbox').find('xmax').text) 69 | ymax = int(object.find('bndbox').find('ymax').text) 70 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 71 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 72 | anthracnose_fruit_rot+=1 73 | 74 | if objectName == 'anthracnose_runner': 75 | xmin = int(object.find('bndbox').find('xmin').text) 76 | ymin = int(object.find('bndbox').find('ymin').text) 77 | xmax = int(object.find('bndbox').find('xmax').text) 78 | ymax = int(object.find('bndbox').find('ymax').text) 79 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 80 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 81 | anthracnose_runner+=1 82 | 83 | if objectName == 'blossom_blight': 84 | xmin = int(object.find('bndbox').find('xmin').text) 85 | ymin = int(object.find('bndbox').find('ymin').text) 86 | xmax = int(object.find('bndbox').find('xmax').text) 87 | ymax = int(object.find('bndbox').find('ymax').text) 88 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 89 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 90 | blossom_blight+=1 91 | 92 | if objectName == 'gray_mold': 93 | xmin = int(object.find('bndbox').find('xmin').text) 94 | ymin = int(object.find('bndbox').find('ymin').text) 95 | xmax = int(object.find('bndbox').find('xmax').text) 96 | ymax = int(object.find('bndbox').find('ymax').text) 97 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 98 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 99 | gray_mold+=1 100 | 101 | if objectName == 'leaf_spot': 102 | xmin = int(object.find('bndbox').find('xmin').text) 103 | ymin = int(object.find('bndbox').find('ymin').text) 104 | xmax = int(object.find('bndbox').find('xmax').text) 105 | ymax = int(object.find('bndbox').find('ymax').text) 106 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 107 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 108 | leaf_spot+=1 109 | 110 | if objectName == 'powdery_mildew_fruit': 111 | xmin = int(object.find('bndbox').find('xmin').text) 112 | ymin = int(object.find('bndbox').find('ymin').text) 113 | xmax = int(object.find('bndbox').find('xmax').text) 114 | ymax = int(object.find('bndbox').find('ymax').text) 115 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 116 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 117 | powdery_mildew_fruit+=1 118 | 119 | if objectName == 'powdery_mildew_leaf': 120 | xmin = int(object.find('bndbox').find('xmin').text) 121 | ymin = int(object.find('bndbox').find('ymin').text) 122 | xmax = int(object.find('bndbox').find('xmax').text) 123 | ymax = int(object.find('bndbox').find('ymax').text) 124 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (255,255,255), text_thickness) 125 | cv2.rectangle(img,(xmin, ymin),(xmax, ymax),color,box_thickness) 126 | powdery_mildew_leaf+=1 127 | 128 | if objectName not in ['anthracnose_runner', 'gray_mold', 'blossom_blight', 129 | 'leaf_spot', 'powdery_mildew_fruit', 'powdery_mildew_leaf', 130 | 'anthracnose_fruit_rot','angular_leafspot']: 131 | xmin = int(object.find('bndbox').find('xmin').text) 132 | ymin = int(object.find('bndbox').find('ymin').text) 133 | xmax = int(object.find('bndbox').find('xmax').text) 134 | ymax = int(object.find('bndbox').find('ymax').text) 135 | cv2.putText(img, objectName, (xmin, ymax), font, text_size, (0, 0, 255), text_thickness) 136 | cv2.rectangle(img, (xmin, ymin), (xmax, ymax), [64, 128, 256], box_thickness) 137 | print('objectName not in these labels. It is :', objectName) 138 | 139 | if len(allObjects) == 0: 140 | print("error") 141 | 142 | name = name.replace('xml', 'jpg') 143 | img_save_path = os.path.join(img_xml, name) 144 | print(img_save_path) 145 | cv2.imwrite(img_save_path, img) 146 | 147 | print("powdery_mildew_leaf boxs is :", powdery_mildew_leaf) 148 | print("angular_leafspot boxs is :", angular_leafspot) 149 | print("anthracnose_fruit_rot boxs is :", anthracnose_fruit_rot) 150 | print("anthracnose_runner boxs is :", anthracnose_runner) 151 | print("blossom_blight boxs is :", blossom_blight) 152 | print("gray_mold boxs is :", gray_mold) 153 | print("leaf_spot boxs is :", leaf_spot) 154 | print("powdery_mildew_fruit boxs is :", powdery_mildew_fruit) 155 | -------------------------------------------------------------------------------- /utils/loggers/__init__.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Logging utils 4 | """ 5 | 6 | import os 7 | import warnings 8 | from threading import Thread 9 | 10 | import pkg_resources as pkg 11 | import torch 12 | from torch.utils.tensorboard import SummaryWriter 13 | 14 | from utils.general import colorstr, emojis 15 | from utils.loggers.wandb.wandb_utils import WandbLogger 16 | from utils.plots import plot_images, plot_results 17 | from utils.torch_utils import de_parallel 18 | 19 | LOGGERS = ('csv', 'tb', 'wandb') # text-file, TensorBoard, Weights & Biases 20 | RANK = int(os.getenv('RANK', -1)) 21 | 22 | try: 23 | import wandb 24 | 25 | assert hasattr(wandb, '__version__') # verify package import not local dir 26 | if pkg.parse_version(wandb.__version__) >= pkg.parse_version('0.12.2') and RANK in [0, -1]: 27 | try: 28 | wandb_login_success = wandb.login(timeout=30) 29 | except wandb.errors.UsageError: # known non-TTY terminal issue 30 | wandb_login_success = False 31 | if not wandb_login_success: 32 | wandb = None 33 | except (ImportError, AssertionError): 34 | wandb = None 35 | 36 | 37 | class Loggers(): 38 | # YOLOv5 Loggers class 39 | def __init__(self, save_dir=None, weights=None, opt=None, hyp=None, logger=None, include=LOGGERS): 40 | self.save_dir = save_dir 41 | self.weights = weights 42 | self.opt = opt 43 | self.hyp = hyp 44 | self.logger = logger # for printing results to console 45 | self.include = include 46 | self.keys = ['train/box_loss', 'train/obj_loss', 'train/cls_loss', # train loss 47 | 'metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95', # metrics 48 | 'val/box_loss', 'val/obj_loss', 'val/cls_loss', # val loss 49 | 'x/lr0', 'x/lr1', 'x/lr2'] # params 50 | self.best_keys = ['best/epoch', 'best/precision', 'best/recall', 'best/mAP_0.5', 'best/mAP_0.5:0.95',] 51 | for k in LOGGERS: 52 | setattr(self, k, None) # init empty logger dictionary 53 | self.csv = True # always log to csv 54 | 55 | # Message 56 | if not wandb: 57 | prefix = colorstr('Weights & Biases: ') 58 | s = f"{prefix}run 'pip install wandb' to automatically track and visualize YOLOv5 🚀 runs (RECOMMENDED)" 59 | print(emojis(s)) 60 | 61 | # TensorBoard 62 | s = self.save_dir 63 | if 'tb' in self.include and not self.opt.evolve: 64 | prefix = colorstr('TensorBoard: ') 65 | self.logger.info(f"{prefix}Start with 'tensorboard --logdir {s.parent}', view at http://localhost:6006/") 66 | self.tb = SummaryWriter(str(s)) 67 | 68 | # W&B 69 | if wandb and 'wandb' in self.include: 70 | wandb_artifact_resume = isinstance(self.opt.resume, str) and self.opt.resume.startswith('wandb-artifact://') 71 | run_id = torch.load(self.weights).get('wandb_id') if self.opt.resume and not wandb_artifact_resume else None 72 | self.opt.hyp = self.hyp # add hyperparameters 73 | self.wandb = WandbLogger(self.opt, run_id) 74 | else: 75 | self.wandb = None 76 | 77 | def on_pretrain_routine_end(self): 78 | # Callback runs on pre-train routine end 79 | paths = self.save_dir.glob('*labels*.jpg') # training labels 80 | if self.wandb: 81 | self.wandb.log({"Labels": [wandb.Image(str(x), caption=x.name) for x in paths]}) 82 | 83 | def on_train_batch_end(self, ni, model, imgs, targets, paths, plots, sync_bn): 84 | # Callback runs on train batch end 85 | if plots: 86 | if ni == 0: 87 | if not sync_bn: # tb.add_graph() --sync known issue https://github.com/ultralytics/yolov5/issues/3754 88 | with warnings.catch_warnings(): 89 | warnings.simplefilter('ignore') # suppress jit trace warning 90 | self.tb.add_graph(torch.jit.trace(de_parallel(model), imgs[0:1], strict=False), []) 91 | if ni < 3: 92 | f = self.save_dir / f'train_batch{ni}.jpg' # filename 93 | Thread(target=plot_images, args=(imgs, targets, paths, f), daemon=True).start() 94 | if self.wandb and ni == 10: 95 | files = sorted(self.save_dir.glob('train*.jpg')) 96 | self.wandb.log({'Mosaics': [wandb.Image(str(f), caption=f.name) for f in files if f.exists()]}) 97 | 98 | def on_train_epoch_end(self, epoch): 99 | # Callback runs on train epoch end 100 | if self.wandb: 101 | self.wandb.current_epoch = epoch + 1 102 | 103 | def on_val_image_end(self, pred, predn, path, names, im): 104 | # Callback runs on val image end 105 | if self.wandb: 106 | self.wandb.val_one_image(pred, predn, path, names, im) 107 | 108 | def on_val_end(self): 109 | # Callback runs on val end 110 | if self.wandb: 111 | files = sorted(self.save_dir.glob('val*.jpg')) 112 | self.wandb.log({"Validation": [wandb.Image(str(f), caption=f.name) for f in files]}) 113 | 114 | def on_fit_epoch_end(self, vals, epoch, best_fitness, fi): 115 | # Callback runs at the end of each fit (train+val) epoch 116 | x = {k: v for k, v in zip(self.keys, vals)} # dict 117 | if self.csv: 118 | file = self.save_dir / 'results.csv' 119 | n = len(x) + 1 # number of cols 120 | s = '' if file.exists() else (('%20s,' * n % tuple(['epoch'] + self.keys)).rstrip(',') + '\n') # add header 121 | with open(file, 'a') as f: 122 | f.write(s + ('%20.5g,' * n % tuple([epoch] + vals)).rstrip(',') + '\n') 123 | 124 | if self.tb: 125 | for k, v in x.items(): 126 | self.tb.add_scalar(k, v, epoch) 127 | 128 | if self.wandb: 129 | if best_fitness == fi: 130 | best_results = [epoch] + vals[3:7] 131 | for i, name in enumerate(self.best_keys): 132 | self.wandb.wandb_run.summary[name] = best_results[i] # log best results in the summary 133 | self.wandb.log(x) 134 | self.wandb.end_epoch(best_result=best_fitness == fi) 135 | 136 | def on_model_save(self, last, epoch, final_epoch, best_fitness, fi): 137 | # Callback runs on model save event 138 | if self.wandb: 139 | if ((epoch + 1) % self.opt.save_period == 0 and not final_epoch) and self.opt.save_period != -1: 140 | self.wandb.log_model(last.parent, self.opt, epoch, fi, best_model=best_fitness == fi) 141 | 142 | def on_train_end(self, last, best, plots, epoch, results): 143 | # Callback runs on training end 144 | if plots: 145 | plot_results(file=self.save_dir / 'results.csv') # save results.png 146 | files = ['results.png', 'confusion_matrix.png', *(f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R'))] 147 | files = [(self.save_dir / f) for f in files if (self.save_dir / f).exists()] # filter 148 | 149 | if self.tb: 150 | import cv2 151 | for f in files: 152 | self.tb.add_image(f.stem, cv2.imread(str(f))[..., ::-1], epoch, dataformats='HWC') 153 | 154 | if self.wandb: 155 | self.wandb.log({k: v for k, v in zip(self.keys[3:10], results)}) # log best.pt val results 156 | self.wandb.log({"Results": [wandb.Image(str(f), caption=f.name) for f in files]}) 157 | # Calling wandb.log. TODO: Refactor this into WandbLogger.log_model 158 | if not self.opt.evolve: 159 | wandb.log_artifact(str(best if best.exists() else last), type='model', 160 | name='run_' + self.wandb.wandb_run.id + '_model', 161 | aliases=['latest', 'best', 'stripped']) 162 | self.wandb.finish_run() 163 | 164 | def on_params_update(self, params): 165 | # Update hyperparams or configs of the experiment 166 | # params: A dict containing {param: value} pairs 167 | if self.wandb: 168 | self.wandb.wandb_run.config.update(params, allow_val_change=True) 169 | -------------------------------------------------------------------------------- /tools/data_augmentation/data_aug_paprika.py: -------------------------------------------------------------------------------- 1 | import random 2 | import xml.etree.ElementTree as ET 3 | import os 4 | import numpy as np 5 | from PIL import Image 6 | import shutil 7 | import imgaug as ia 8 | from imgaug import augmenters as iaa 9 | from tqdm import tqdm 10 | ia.seed(1) 11 | 12 | 13 | SAVE = True 14 | 15 | 16 | def read_xml_annotation(root, image_id): 17 | in_file = open(os.path.join(root, image_id)) 18 | tree = ET.parse(in_file) 19 | root = tree.getroot() 20 | bndboxlist = [] 21 | 22 | for object in root.findall('object'): # 找到root节点下的所有country节点 23 | bndbox = object.find('bndbox') # 子节点下节点rank的值 24 | 25 | xmin = int(bndbox.find('xmin').text) 26 | xmax = int(bndbox.find('xmax').text) 27 | ymin = int(bndbox.find('ymin').text) 28 | ymax = int(bndbox.find('ymax').text) 29 | # print(xmin,ymin,xmax,ymax) 30 | bndboxlist.append([xmin, ymin, xmax, ymax]) 31 | # print(bndboxlist) 32 | 33 | # bndbox = root.find('object').find('bndbox') 34 | return bndboxlist 35 | 36 | 37 | # (506.0000, 330.0000, 528.0000, 348.0000) -> (520.4747, 381.5080, 540.5596, 398.6603) 38 | def change_xml_annotation(root, image_id, new_target): 39 | new_xmin = new_target[0] 40 | new_ymin = new_target[1] 41 | new_xmax = new_target[2] 42 | new_ymax = new_target[3] 43 | 44 | in_file = open(os.path.join(root, str(image_id) + '.xml')) # 这里root分别由两个意思 45 | tree = ET.parse(in_file) 46 | xmlroot = tree.getroot() 47 | object = xmlroot.find('object') 48 | bndbox = object.find('bndbox') 49 | xmin = bndbox.find('xmin') 50 | xmin.text = str(new_xmin) 51 | ymin = bndbox.find('ymin') 52 | ymin.text = str(new_ymin) 53 | xmax = bndbox.find('xmax') 54 | xmax.text = str(new_xmax) 55 | ymax = bndbox.find('ymax') 56 | ymax.text = str(new_ymax) 57 | tree.write(os.path.join(root, str("%06s" % (str(id) + '.xml')))) 58 | 59 | 60 | def change_xml_list_annotation(root, image_id, new_target, saveroot, id): 61 | in_file = open(os.path.join(root, str(image_id) + '.xml')) # 这里root分别由两个意思 62 | tree = ET.parse(in_file) 63 | elem = tree.find('filename') 64 | id = str(id) 65 | elem.text = str("%06s" % id) + '.jpg' 66 | xmlroot = tree.getroot() 67 | index = 0 68 | 69 | for object in xmlroot.findall('object'): # 找到root节点下的所有country节点 70 | bndbox = object.find('bndbox') # 子节点下节点rank的值 71 | xmin_org = int(bndbox.find('xmin').text) 72 | xmax_org = int(bndbox.find('xmax').text) 73 | ymin_org = int(bndbox.find('ymin').text) 74 | ymax_org = int(bndbox.find('ymax').text) 75 | 76 | area_org = int((xmax_org - xmin_org) * (ymax_org - ymin_org)) 77 | 78 | new_xmin = new_target[index][0] 79 | new_ymin = new_target[index][1] 80 | new_xmax = new_target[index][2] 81 | new_ymax = new_target[index][3] 82 | 83 | area_new = int((new_xmax - new_xmin) * (new_ymax -new_ymin)) 84 | 85 | if new_ymax <= new_ymin or new_xmax <= new_xmin: 86 | xmlroot.remove(object) 87 | elif abs(area_new) <= abs(area_org/2): 88 | xmlroot.remove(object) 89 | else: 90 | xmin = bndbox.find('xmin') 91 | xmin.text = str(new_xmin) 92 | ymin = bndbox.find('ymin') 93 | ymin.text = str(new_ymin) 94 | xmax = bndbox.find('xmax') 95 | xmax.text = str(new_xmax) 96 | ymax = bndbox.find('ymax') 97 | ymax.text = str(new_ymax) 98 | 99 | index = index + 1 100 | 101 | tree.write(os.path.join(saveroot, str("%06s" % id) + '.xml')) 102 | 103 | 104 | def mkdir(path): 105 | # 去除首位空格 106 | path = path.strip() 107 | # 去除尾部 \ 符号 108 | path = path.rstrip("\\") 109 | # 判断路径是否存在 110 | # 存在 True 111 | # 不存在 False 112 | isExists = os.path.exists(path) 113 | # 判断结果 114 | if not isExists: 115 | # 如果不存在则创建目录 116 | # 创建目录操作函数 117 | os.makedirs(path) 118 | print(path + ' 创建成功') 119 | return True 120 | else: 121 | # 如果目录存在则不创建,并提示目录已存在 122 | print(path + ' 目录已存在') 123 | return False 124 | 125 | 126 | if __name__ == "__main__": 127 | # 原始的img与xml文件路径 128 | set='val' 129 | IMG_DIR = "/Users/Dong/Documents/test/dataset_org/"+set 130 | XML_DIR = "/Users/Dong/Documents/test/dataset_org/"+set+"_xml" 131 | # 存储增强后的XML文件夹路径 132 | AUG_XML_DIR = "/Users/Dong/Documents/test/dataset_aug/"+set+"_xml" 133 | try: 134 | shutil.rmtree(AUG_XML_DIR) 135 | except FileNotFoundError as e: 136 | a = 1 137 | mkdir(AUG_XML_DIR) 138 | # 存储增强后的影像文件夹路径 139 | AUG_IMG_DIR = "/Users/Dong/Documents/test/dataset_aug/"+set 140 | try: 141 | shutil.rmtree(AUG_IMG_DIR) 142 | except FileNotFoundError as e: 143 | a = 1 144 | mkdir(AUG_IMG_DIR) 145 | # 每张影像增强的数量 146 | AUGLOOP = 0 147 | 148 | boxes_img_aug_list = [] 149 | new_bndbox = [] 150 | new_bndbox_list = [] 151 | 152 | # 影像增强 153 | seq = iaa.Sequential([ 154 | iaa.Flipud(0.5), # 对50%的图像做镜像翻转 155 | #iaa.Rotate(-5,5), 156 | # iaa.ContrastNormalization((0.75, 1.5), per_channel=True), 157 | iaa.Crop(percent=(0, 0.05), keep_size=True), 158 | # iaa.Multiply((1.2, 1.5)), # 改变亮度 159 | iaa.GaussianBlur(sigma=(0, 0.5)), # iaa.GaussianBlur(0.5), 160 | iaa.Affine( 161 | translate_percent={"x": (-0.05, 0.05), "y": (-0.05, 0.05)}, 162 | scale={"x": (0.95, 1.05), "y": (0.95, 1.05)}, 163 | rotate=(-90, -90) 164 | ) # 对一部分图像做仿射变换, rotate旋转±30度之间, scale图像缩放为80%到95%之间, translate_px 独立地在x轴和y轴上将图像平移到15像素 165 | ]) 166 | seq2 = iaa.Sequential([ 167 | iaa.Flipud(0.5), # 对50%的图像做镜像翻转 168 | # iaa.ContrastNormalization((0.75, 1.5), per_channel=True), 169 | iaa.Crop(percent=(0, 0.05), keep_size=True), 170 | # iaa.Multiply((1.2, 1.5)), # 改变亮度 171 | iaa.GaussianBlur(sigma=(0, 0.5)), # iaa.GaussianBlur(0.5), 172 | iaa.Affine( 173 | translate_percent={"x": (-0.05, 0.05), "y": (-0.05, 0.05)}, 174 | scale={"x": (0.95, 1.05), "y": (0.95, 1.05)}, 175 | rotate=(90, 90) 176 | ) # 对一部分图像做仿射变换, rotate旋转±30度之间, scale图像缩放为80%到95%之间, translate_px 独立地在x轴和y轴上将图像平移到15像素 177 | ]) 178 | 179 | # os.walk() 方法用于通过在目录树中游走输出在目录中的文件名,向上或者向下 180 | # root所指的是当前正在遍历的这个文件夹的本身的地址 181 | # sub_folders 是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录) 182 | # files 同样是 list , 内容是该文件夹中所有的文件(不包括子目录) 183 | for root, sub_folders, files in os.walk(XML_DIR): 184 | for name in tqdm(files): 185 | bndbox = read_xml_annotation(XML_DIR, name) 186 | if len(bndbox) == 0: 187 | print("There is no bounding box : ", name) 188 | continue 189 | shutil.copy(os.path.join(XML_DIR, name), AUG_XML_DIR) 190 | shutil.copy(os.path.join(IMG_DIR, name[:-4] + '.jpg'), AUG_IMG_DIR) 191 | if "blossom_end_rot" in name: 192 | AUGLOOP = 1 193 | elif "graymold" in name: 194 | AUGLOOP = 4 195 | elif "powdery_mildew" in name: 196 | AUGLOOP = 4 197 | elif "spider_mite" in name: 198 | AUGLOOP = 4 199 | elif "spotting_disease" in name: 200 | AUGLOOP = 0 201 | else: 202 | print("wrong?!",name) 203 | # continue 204 | for epoch in range(AUGLOOP): 205 | a = random.randint(0,1) 206 | if a ==0: 207 | seq_det = seq.to_deterministic() 208 | else: 209 | seq_det = seq2.to_deterministic() # 保持坐标和图像同步改变,而不是随机 210 | 211 | # 读取图片 212 | img = Image.open(os.path.join(IMG_DIR, name[:-4] + '.jpg')) 213 | # sp = img.size 214 | img = np.asarray(img) 215 | # bndbox 坐标增强 216 | for i in range(len(bndbox)): 217 | bbs = ia.BoundingBoxesOnImage([ 218 | ia.BoundingBox(x1=bndbox[i][0], y1=bndbox[i][1], x2=bndbox[i][2], y2=bndbox[i][3]), 219 | ], shape=img.shape) 220 | 221 | bbs_aug = seq_det.augment_bounding_boxes([bbs])[0] 222 | boxes_img_aug_list.append(bbs_aug) 223 | 224 | # new_bndbox_list:[[x1,y1,x2,y2],...[],[]] 225 | n_x1 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x1))) 226 | n_y1 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y1))) 227 | n_x2 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x2))) 228 | n_y2 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y2))) 229 | if n_x1 == 1 and n_x1 == n_x2: 230 | n_x2 += 1 231 | if n_y1 == 1 and n_y2 == n_y1: 232 | n_y2 += 1 233 | if n_x1 >= n_x2 or n_y1 >= n_y2: 234 | print('error', name) 235 | SAVE = False 236 | new_bndbox_list.append([n_x1, n_y1, n_x2, n_y2]) 237 | if len(new_bndbox_list) == 0: 238 | SAVE = False 239 | if SAVE: 240 | # 存储变化后的图片 241 | image_aug = seq_det.augment_images([img])[0] 242 | path = os.path.join(AUG_IMG_DIR, str(name[:-4]) + str(epoch) + '.jpg') 243 | image_auged = bbs.draw_on_image(image_aug, thickness=0) 244 | Image.fromarray(image_auged).convert('RGB').save(path) 245 | 246 | # 存储变化后的XML 247 | change_xml_list_annotation(XML_DIR, name[:-4], new_bndbox_list, AUG_XML_DIR, 248 | str(name[:-4]) + str(epoch)) 249 | # print(str(name[:-4]) + str(epoch) + '.jpg') 250 | new_bndbox_list = [] 251 | else: 252 | print("skip this image, SAVE is ", SAVE) 253 | new_bndbox_list = [] 254 | SAVE = True 255 | -------------------------------------------------------------------------------- /tools/data_augmentation/data_aug.py: -------------------------------------------------------------------------------- 1 | import random 2 | import xml.etree.ElementTree as ET 3 | import os 4 | import numpy as np 5 | from PIL import Image 6 | import shutil 7 | import imgaug as ia 8 | from imgaug import augmenters as iaa 9 | from tqdm import tqdm 10 | ia.seed(1) 11 | 12 | 13 | SAVE = True 14 | 15 | 16 | def read_xml_annotation(root, image_id): 17 | in_file = open(os.path.join(root, image_id)) 18 | tree = ET.parse(in_file) 19 | root = tree.getroot() 20 | bndboxlist = [] 21 | 22 | for object in root.findall('object'): # 找到root节点下的所有country节点 23 | bndbox = object.find('bndbox') # 子节点下节点rank的值 24 | 25 | xmin = int(bndbox.find('xmin').text) 26 | xmax = int(bndbox.find('xmax').text) 27 | ymin = int(bndbox.find('ymin').text) 28 | ymax = int(bndbox.find('ymax').text) 29 | # print(xmin,ymin,xmax,ymax) 30 | bndboxlist.append([xmin, ymin, xmax, ymax]) 31 | # print(bndboxlist) 32 | 33 | # bndbox = root.find('object').find('bndbox') 34 | return bndboxlist 35 | 36 | 37 | # (506.0000, 330.0000, 528.0000, 348.0000) -> (520.4747, 381.5080, 540.5596, 398.6603) 38 | def change_xml_annotation(root, image_id, new_target): 39 | new_xmin = new_target[0] 40 | new_ymin = new_target[1] 41 | new_xmax = new_target[2] 42 | new_ymax = new_target[3] 43 | 44 | in_file = open(os.path.join(root, str(image_id) + '.xml')) # 这里root分别由两个意思 45 | tree = ET.parse(in_file) 46 | xmlroot = tree.getroot() 47 | object = xmlroot.find('object') 48 | bndbox = object.find('bndbox') 49 | xmin = bndbox.find('xmin') 50 | xmin.text = str(new_xmin) 51 | ymin = bndbox.find('ymin') 52 | ymin.text = str(new_ymin) 53 | xmax = bndbox.find('xmax') 54 | xmax.text = str(new_xmax) 55 | ymax = bndbox.find('ymax') 56 | ymax.text = str(new_ymax) 57 | tree.write(os.path.join(root, str("%06s" % (str(id) + '.xml')))) 58 | 59 | 60 | def change_xml_list_annotation(root, image_id, new_target, saveroot, id): 61 | in_file = open(os.path.join(root, str(image_id) + '.xml')) # 这里root分别由两个意思 62 | tree = ET.parse(in_file) 63 | elem = tree.find('filename') 64 | id = str(id) 65 | elem.text = str("%06s" % id) + '.jpg' 66 | xmlroot = tree.getroot() 67 | index = 0 68 | 69 | for object in xmlroot.findall('object'): # 找到root节点下的所有country节点 70 | bndbox = object.find('bndbox') # 子节点下节点rank的值 71 | xmin_org = int(bndbox.find('xmin').text) 72 | xmax_org = int(bndbox.find('xmax').text) 73 | ymin_org = int(bndbox.find('ymin').text) 74 | ymax_org = int(bndbox.find('ymax').text) 75 | 76 | area_org = int((xmax_org - xmin_org) * (ymax_org - ymin_org)) 77 | 78 | new_xmin = new_target[index][0] 79 | new_ymin = new_target[index][1] 80 | new_xmax = new_target[index][2] 81 | new_ymax = new_target[index][3] 82 | 83 | area_new = int((new_xmax - new_xmin) * (new_ymax -new_ymin)) 84 | 85 | if new_ymax <= new_ymin or new_xmax <= new_xmin: 86 | xmlroot.remove(object) 87 | elif abs(area_new) <= abs(area_org/2): 88 | xmlroot.remove(object) 89 | else: 90 | xmin = bndbox.find('xmin') 91 | xmin.text = str(new_xmin) 92 | ymin = bndbox.find('ymin') 93 | ymin.text = str(new_ymin) 94 | xmax = bndbox.find('xmax') 95 | xmax.text = str(new_xmax) 96 | ymax = bndbox.find('ymax') 97 | ymax.text = str(new_ymax) 98 | 99 | index = index + 1 100 | 101 | tree.write(os.path.join(saveroot, str("%06s" % id) + '.xml')) 102 | 103 | 104 | def mkdir(path): 105 | # 去除首位空格 106 | path = path.strip() 107 | # 去除尾部 \ 符号 108 | path = path.rstrip("\\") 109 | # 判断路径是否存在 110 | # 存在 True 111 | # 不存在 False 112 | isExists = os.path.exists(path) 113 | # 判断结果 114 | if not isExists: 115 | # 如果不存在则创建目录 116 | # 创建目录操作函数 117 | os.makedirs(path) 118 | print(path + ' 创建成功') 119 | return True 120 | else: 121 | # 如果目录存在则不创建,并提示目录已存在 122 | print(path + ' 目录已存在') 123 | return False 124 | 125 | 126 | if __name__ == "__main__": 127 | # 原始的img与xml文件路径 128 | set='val' 129 | IMG_DIR = "/Users/Dong/Desktop/test/dataset_org/"+set 130 | XML_DIR = "/Users/Dong/Desktop/test/dataset_org/"+set+"_xml" 131 | # 存储增强后的XML文件夹路径 132 | AUG_XML_DIR = "/Users/Dong/Desktop/test/dataset_aug/"+set+"_xml" 133 | try: 134 | shutil.rmtree(AUG_XML_DIR) 135 | except FileNotFoundError as e: 136 | a = 1 137 | mkdir(AUG_XML_DIR) 138 | # 存储增强后的影像文件夹路径 139 | AUG_IMG_DIR = "/Users/Dong/Desktop/test/dataset_aug/"+set 140 | try: 141 | shutil.rmtree(AUG_IMG_DIR) 142 | except FileNotFoundError as e: 143 | a = 1 144 | mkdir(AUG_IMG_DIR) 145 | # 每张影像增强的数量 146 | AUGLOOP = 0 147 | 148 | boxes_img_aug_list = [] 149 | new_bndbox = [] 150 | new_bndbox_list = [] 151 | 152 | # 影像增强 153 | seq = iaa.Sequential([ 154 | iaa.Flipud(0.5), # 对50%的图像做镜像翻转 155 | #iaa.Rotate(-5,5), 156 | # iaa.ContrastNormalization((0.75, 1.5), per_channel=True), 157 | iaa.Crop(percent=(0, 0.05), keep_size=True), 158 | # iaa.Multiply((1.2, 1.5)), # 改变亮度 159 | iaa.GaussianBlur(sigma=(0, 0.5)), # iaa.GaussianBlur(0.5), 160 | iaa.Affine( 161 | translate_percent={"x": (-0.05, 0.05), "y": (-0.05, 0.05)}, 162 | scale={"x": (0.95, 1.05), "y": (0.95, 1.05)}, 163 | rotate=(-90, -90) 164 | ) # 对一部分图像做仿射变换, rotate旋转±30度之间, scale图像缩放为80%到95%之间, translate_px 独立地在x轴和y轴上将图像平移到15像素 165 | ]) 166 | seq2 = iaa.Sequential([ 167 | iaa.Flipud(0.5), # 对50%的图像做镜像翻转 168 | # iaa.ContrastNormalization((0.75, 1.5), per_channel=True), 169 | iaa.Crop(percent=(0, 0.05), keep_size=True), 170 | # iaa.Multiply((1.2, 1.5)), # 改变亮度 171 | iaa.GaussianBlur(sigma=(0, 0.5)), # iaa.GaussianBlur(0.5), 172 | iaa.Affine( 173 | translate_percent={"x": (-0.05, 0.05), "y": (-0.05, 0.05)}, 174 | scale={"x": (0.95, 1.05), "y": (0.95, 1.05)}, 175 | rotate=(90, 90) 176 | ) # 对一部分图像做仿射变换, rotate旋转±30度之间, scale图像缩放为80%到95%之间, translate_px 独立地在x轴和y轴上将图像平移到15像素 177 | ]) 178 | 179 | # os.walk() 方法用于通过在目录树中游走输出在目录中的文件名,向上或者向下 180 | # root所指的是当前正在遍历的这个文件夹的本身的地址 181 | # sub_folders 是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录) 182 | # files 同样是 list , 内容是该文件夹中所有的文件(不包括子目录) 183 | for root, sub_folders, files in os.walk(XML_DIR): 184 | for name in tqdm(files): 185 | bndbox = read_xml_annotation(XML_DIR, name) 186 | if len(bndbox) == 0: 187 | print("There is no bounding box : ", name) 188 | continue 189 | shutil.copy(os.path.join(XML_DIR, name), AUG_XML_DIR) 190 | shutil.copy(os.path.join(IMG_DIR, name[:-4] + '.jpg'), AUG_IMG_DIR) 191 | if "anthracnose_runner" in name: 192 | AUGLOOP = 6 193 | elif "gray_mold" in name: 194 | AUGLOOP = 1 195 | elif "blossom_blight" in name: 196 | AUGLOOP = 1 197 | elif "leaf_spot" in name: 198 | AUGLOOP = 1 199 | elif "powdery_mildew_fruit" in name: 200 | AUGLOOP = 5 201 | elif "powdery_mildew_leaf" in name: 202 | AUGLOOP = 1 203 | elif "anthracnose_fruit_rot" in name: 204 | AUGLOOP = 9 205 | elif "angular_leafspot" in name: 206 | AUGLOOP = 3 207 | else: 208 | print("wrong?!",name) 209 | # continue 210 | for epoch in range(AUGLOOP): 211 | a = random.randint(0,1) 212 | if a ==0: 213 | seq_det = seq.to_deterministic() 214 | else: 215 | seq_det = seq2.to_deterministic() # 保持坐标和图像同步改变,而不是随机 216 | 217 | # 读取图片 218 | img = Image.open(os.path.join(IMG_DIR, name[:-4] + '.jpg')) 219 | # sp = img.size 220 | img = np.asarray(img) 221 | # bndbox 坐标增强 222 | for i in range(len(bndbox)): 223 | bbs = ia.BoundingBoxesOnImage([ 224 | ia.BoundingBox(x1=bndbox[i][0], y1=bndbox[i][1], x2=bndbox[i][2], y2=bndbox[i][3]), 225 | ], shape=img.shape) 226 | 227 | bbs_aug = seq_det.augment_bounding_boxes([bbs])[0] 228 | boxes_img_aug_list.append(bbs_aug) 229 | 230 | # new_bndbox_list:[[x1,y1,x2,y2],...[],[]] 231 | n_x1 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x1))) 232 | n_y1 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y1))) 233 | n_x2 = int(max(1, min(img.shape[1], bbs_aug.bounding_boxes[0].x2))) 234 | n_y2 = int(max(1, min(img.shape[0], bbs_aug.bounding_boxes[0].y2))) 235 | if n_x1 == 1 and n_x1 == n_x2: 236 | n_x2 += 1 237 | if n_y1 == 1 and n_y2 == n_y1: 238 | n_y2 += 1 239 | if n_x1 >= n_x2 or n_y1 >= n_y2: 240 | print('error', name) 241 | SAVE = False 242 | new_bndbox_list.append([n_x1, n_y1, n_x2, n_y2]) 243 | if len(new_bndbox_list) == 0: 244 | SAVE = False 245 | if SAVE: 246 | # 存储变化后的图片 247 | image_aug = seq_det.augment_images([img])[0] 248 | path = os.path.join(AUG_IMG_DIR, str(name[:-4]) + str(epoch) + '.jpg') 249 | image_auged = bbs.draw_on_image(image_aug, thickness=0) 250 | Image.fromarray(image_auged).convert('RGB').save(path) 251 | 252 | # 存储变化后的XML 253 | change_xml_list_annotation(XML_DIR, name[:-4], new_bndbox_list, AUG_XML_DIR, 254 | str(name[:-4]) + str(epoch)) 255 | # print(str(name[:-4]) + str(epoch) + '.jpg') 256 | new_bndbox_list = [] 257 | else: 258 | print("skip this image, SAVE is ", SAVE) 259 | new_bndbox_list = [] 260 | SAVE = True 261 | --------------------------------------------------------------------------------