├── Blood-Detector ├── __init__.py ├── blood_detection.py ├── create_csv.py ├── data_manage.py ├── data_viz.py ├── export_jit.py ├── runs │ ├── FINAL │ │ ├── events.out.tfevents.1605062814.MSI.1800.0 │ │ ├── hyp.yaml │ │ ├── labels.png │ │ ├── opt.yaml │ │ ├── precision-recall_curve.png │ │ ├── results.png │ │ ├── results.txt │ │ ├── test_batch0_gt.jpg │ │ ├── test_batch0_pred.jpg │ │ ├── train_batch0.jpg │ │ ├── train_batch1.jpg │ │ ├── train_batch2.jpg │ │ └── weights │ │ │ └── best.pt │ └── __init__.py ├── train.py └── web │ ├── index.py │ ├── static │ ├── css │ │ └── style.css │ ├── images │ │ ├── README │ │ │ └── brandlogo.png │ │ ├── blog │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ └── alexander.png │ │ ├── hero │ │ │ ├── 1.jpg │ │ │ ├── 1_1.jpg │ │ │ ├── final_mobile.gif │ │ │ ├── final_pc.png │ │ │ ├── github_logo.png │ │ │ ├── logo.png │ │ │ ├── paper_logo.png │ │ │ └── text_enigma.png │ │ └── skill.jpg │ ├── inference │ │ ├── input │ │ │ ├── test_image.jpg │ │ │ └── test_video.mp4 │ │ └── output │ │ │ └── test_image.jpg │ └── js │ │ └── script.js │ └── templates │ └── index.html ├── Dataset ├── BCCD │ ├── Annotations │ │ └── __init__.py │ ├── JPEGImages │ │ └── __init__.py │ └── ModelData │ │ ├── images │ │ ├── train │ │ │ └── __init__.py │ │ └── val │ │ │ └── __init__.py │ │ └── labels │ │ ├── train.cache │ │ ├── train │ │ └── __init__.py │ │ ├── val.cache │ │ └── val │ │ └── __init__.py ├── LICENSE └── bloodData.yaml ├── LICENSE ├── README.md ├── YOLOv5 ├── LICENSE ├── data │ ├── coco.yaml │ ├── coco128.yaml │ ├── hyp.finetune.yaml │ ├── hyp.scratch.yaml │ ├── scripts │ │ ├── get_coco.sh │ │ └── get_voc.sh │ └── voc.yaml ├── detect.py ├── models │ ├── __init__.py │ ├── common.py │ ├── experimental.py │ ├── export.py │ ├── hub │ │ ├── yolov3-spp.yaml │ │ ├── yolov5-fpn.yaml │ │ └── yolov5-panet.yaml │ ├── yolo.py │ ├── yolov5l.yaml │ ├── yolov5m.yaml │ ├── yolov5s.yaml │ └── yolov5x.yaml ├── sotabench.py ├── test.py ├── train.py └── utils │ ├── __init__.py │ ├── activations.py │ ├── datasets.py │ ├── evolve.sh │ ├── general.py │ ├── google_app_engine │ ├── Dockerfile │ ├── additional_requirements.txt │ └── app.yaml │ ├── google_utils.py │ └── torch_utils.py └── requirements.txt /Blood-Detector/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/__init__.py -------------------------------------------------------------------------------- /Blood-Detector/blood_detection.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def detection(file_name): 4 | 5 | # correction in the line 53 in export.py from original repo 6 | 7 | return os.system('python ./../../YOLOv5/detect.py --source ./static/inference/input/{} --output \ 8 | ./static/inference/output/ --weights \ 9 | ./../runs/FINAL/weights/best.pt --device cpu'.format(file_name)) 10 | 11 | if __name__ == '__main__': 12 | detection() 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Blood-Detector/create_csv.py: -------------------------------------------------------------------------------- 1 | #DATA MANAGE 2 | 3 | import pandas as pd 4 | import os 5 | import xml.etree.ElementTree as ET 6 | from glob import glob 7 | 8 | ''' 9 | Programa para creación del documento CSV requerido en data_viz.py 10 | ''' 11 | 12 | annotations = sorted(glob('./../Dataset/BCCD/Annotations/*.xml')) # importamos el dataset de las anotaciones XML 13 | dframe=[] 14 | 15 | print("Cantidad leída: {} archivos".format(len(annotations))) 16 | label_f = lambda x: 0 if x=='RBC' else (1 if x=='WBC' else 2) # asignamos enteros a las 3 posibles etiquetas 17 | 18 | # Dimensiones de las imágenes del Dataset 19 | img_width = 640 20 | img_height = 480 21 | 22 | for file in annotations: # para recorrer la carpeta Annotations con los XML respectivos 23 | 24 | file_name = file.split('/')[-1].split('\\')[-1].split('.')[0] + '.jpg' # '\' corregido para windows 25 | tree = ET.parse(file) 26 | root = tree.getroot().iter('object') 27 | row = [] 28 | 29 | for node in root: # buscamos en cada .XML los parámetros requeridos 30 | 31 | name_cell = node.find('name').text 32 | xmin = int(node.find("bndbox/xmin").text) 33 | ymin = int(node.find("bndbox/ymin").text) 34 | xmax = int(node.find("bndbox/xmax").text) 35 | ymax = int(node.find("bndbox/ymax").text) 36 | 37 | # 'round' redondea el flotante a precisión 5 38 | width_norm = round(float((xmax - xmin)/img_width), 5) # anchura normalizada [0, 1] 39 | height_norm = round(float((ymax - ymin)/img_height), 5) # altura normalizada [0, 1] 40 | x_center_norm = round(float ((xmin/img_width) + (width_norm/2.)), 5) # normalizamos el xmin para sumar con with_norm/2 y hallar centroide X 41 | y_center_norm = round(float((ymin/img_height) + (height_norm/2.)), 5) # normalizamos el ymin para sumar con height_norm/2 y hallar centroide Y 42 | 43 | 44 | label = int(label_f(name_cell)) # usamos la función anónima para asignar una etiqueta a la clase 45 | 46 | row = [file_name, name_cell, label, xmin, ymin, xmax, ymax, width_norm, height_norm, x_center_norm, y_center_norm] # llenamos nuestra lista fila 47 | dframe.append(row) # añadimos nuestra lista fila a la lista dframe 48 | 49 | dataframe = pd.DataFrame(dframe, columns = ["file_name", "name_cell", "label", "xmin", "ymin", "xmax", "ymax", "width_norm", "height_norm", "x_center_norm", "y_center_norm"]) 50 | dataframe[["file_name", "name_cell", "label", "xmin", "ymin", "xmax", "ymax", "width_norm", "height_norm", "x_center_norm", "y_center_norm"]].to_csv('./../Dataset/annotations_blood_cells.csv', index = False) 51 | -------------------------------------------------------------------------------- /Blood-Detector/data_manage.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from sklearn import model_selection 3 | import numpy as np 4 | import os 5 | import shutil 6 | 7 | def create_txts(df, image_original_path, image_train_path, label_path): 8 | 9 | filenames = [] 10 | for filename in df.file_name: 11 | filenames.append(filename) 12 | unique_files = set(filenames) 13 | 14 | for unique in unique_files: 15 | yolo_input = [] 16 | for _, row in df[df.file_name == unique].iterrows(): 17 | yolo_input.append([row.label, row.x_center_norm, row.y_center_norm, row.width_norm, row.height_norm]) 18 | yolo_input_to_txt = np.array(yolo_input) 19 | name_txt = os.path.join(label_path, str(row.file_name.split('.')[0] + ".txt")) 20 | 21 | np.savetxt(name_txt, yolo_input_to_txt, fmt=["%d", "%f", "%f", "%f", "%f"]) 22 | shutil.copyfile(os.path.join(image_original_path, row.file_name), os.path.join(image_train_path, row.file_name)) 23 | 24 | 25 | def preprocessing(): 26 | 27 | dframe = pd.read_csv('./../Dataset/annotations_blood_cells.csv') 28 | df_train, df_valid = model_selection.train_test_split(dframe, test_size=0.1, random_state=13, shuffle=True) 29 | print('Shape train: {}'.format(df_train.shape)) 30 | print('Shape valid: {}'.format(df_valid.shape)) 31 | 32 | image_original_path = './../Dataset/BCCD/JPEGImages' 33 | 34 | image_train_path = './../Dataset/BCCD/ModelData/images/train' 35 | image_val_path = './../Dataset/BCCD/ModelData/images/val' 36 | 37 | labels_train_path = './../Dataset/BCCD/ModelData/labels/train' 38 | labels_val_path = './../Dataset/BCCD/ModelData/labels/val' 39 | 40 | 41 | create_txts(df_train, image_original_path, image_train_path, labels_train_path) 42 | create_txts(df_valid, image_original_path, image_val_path, labels_val_path) 43 | 44 | 45 | 46 | 47 | if __name__ == '__main__': 48 | preprocessing() -------------------------------------------------------------------------------- /Blood-Detector/data_viz.py: -------------------------------------------------------------------------------- 1 | """ Ploter images with bounding boxes """ 2 | # python data_viz.py --file_image BloodImage_00073.jpg 3 | 4 | import cv2 5 | import matplotlib.pyplot as plt 6 | import sys 7 | import pandas as pd 8 | import argparse 9 | 10 | def visualization(image_name): 11 | 12 | dframe = pd.read_csv('./../Dataset/annotations_blood_cells.csv') 13 | image = cv2.imread('./../Dataset/BCCD/JPEGImages/{}'.format(image_name)) 14 | image = cv2.cvtColor(image ,cv2.COLOR_BGR2RGB) 15 | 16 | for _, row in dframe[dframe.file_name == '{}'.format(image_name)].iterrows(): 17 | 18 | xmin = row.xmin 19 | xmax = row.xmax 20 | ymin = row.ymin 21 | ymax = row.ymax 22 | 23 | width = xmax - xmin 24 | height = ymax - ymin 25 | 26 | if row.label == 0: # glob. rojos 27 | cv2.rectangle(image, (xmin, ymin),(xmin + width, ymin + height), (216, 65, 38), 2) 28 | 29 | if row.label == 1: # glob. blanco 30 | cv2.rectangle(image, (xmin, ymin),(xmin + width, ymin + height), (219, 223, 198), 2) 31 | 32 | if row.label == 2: # plaquetas 33 | cv2.rectangle(image, (xmin, ymin),(xmin + width, ymin + height), (203, 203, 53), 2) 34 | 35 | cv2.putText(image, row.name_cell, (xmin, ymin + height + 20), cv2.FONT_HERSHEY_SIMPLEX , 0.5, (0, 0, 0), 1, cv2.LINE_AA) 36 | 37 | return image 38 | 39 | if __name__ == '__main__': 40 | 41 | parser = argparse.ArgumentParser(description="Plot image select") 42 | parser.add_argument('--file_image', type=str, default='BloodImage_00031.jpg', required=False, 43 | help='image to plot with bounding boxes') 44 | 45 | args = parser.parse_args() 46 | 47 | frame_image = visualization(args.file_image) 48 | plt.imshow(frame_image) 49 | plt.show() 50 | -------------------------------------------------------------------------------- /Blood-Detector/export_jit.py: -------------------------------------------------------------------------------- 1 | # pip install coremltools==4.0b2 2 | # pip install -r requirements.txt 3 | # pip install onnx>=1.7.0 4 | 5 | import os 6 | 7 | def export_JIT(): 8 | 9 | # correction in the line 53 in export.py from original repo 10 | os.system('python ./../YOLOv5/models/export.py --weights ./runs/FINAL/weights/best.pt --img 640 --batch 1') 11 | 12 | 13 | if __name__ == '__main__': 14 | export_JIT() -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/events.out.tfevents.1605062814.MSI.1800.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/events.out.tfevents.1605062814.MSI.1800.0 -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/hyp.yaml: -------------------------------------------------------------------------------- 1 | lr0: 0.01 2 | lrf: 0.2 3 | momentum: 0.937 4 | weight_decay: 0.0005 5 | warmup_epochs: 3.0 6 | warmup_momentum: 0.8 7 | warmup_bias_lr: 0.1 8 | box: 0.05 9 | cls: 0.5 10 | cls_pw: 1.0 11 | obj: 1.0 12 | obj_pw: 1.0 13 | iou_t: 0.2 14 | anchor_t: 4.0 15 | fl_gamma: 0.0 16 | hsv_h: 0.015 17 | hsv_s: 0.7 18 | hsv_v: 0.4 19 | degrees: 0.0 20 | translate: 0.1 21 | scale: 0.5 22 | shear: 0.0 23 | perspective: 0.0 24 | flipud: 0.0 25 | fliplr: 0.5 26 | mosaic: 1.0 27 | mixup: 0.0 28 | -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/labels.png -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/opt.yaml: -------------------------------------------------------------------------------- 1 | type_q: static 2 | logdir: runs/exp7_FINAL 3 | cfg: ./../Dataset/bloodData.yaml 4 | hyp: ./../yolo-v5/data/hyp.scratch.yaml 5 | -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/precision-recall_curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/precision-recall_curve.png -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/results.png -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/results.txt: -------------------------------------------------------------------------------- 1 | 0/99 2.62G 0.1052 0.1539 0.03458 0.2936 276 640 0 0 0.001692 0.0003678 0.09266 0.04046 0.03643 2 | 1/99 2.99G 0.09433 0.1693 0.03023 0.2939 369 640 0.005072 0.004095 0.005896 0.001191 0.0849 0.04875 0.03005 3 | 2/99 2.99G 0.08737 0.161 0.02551 0.2738 264 640 0.004443 0.2629 0.0106 0.002567 0.07932 0.07315 0.02514 4 | 3/99 2.99G 0.07952 0.1508 0.02116 0.2514 268 640 0.004701 0.4259 0.0162 0.004538 0.07181 0.09602 0.02192 5 | 4/99 2.99G 0.07083 0.1509 0.01757 0.2393 307 640 0.005836 0.5266 0.02609 0.008426 0.06462 0.1132 0.01852 6 | 5/99 2.99G 0.06108 0.146 0.01542 0.2225 247 640 0.008292 0.5946 0.04227 0.01476 0.0558 0.1095 0.01736 7 | 6/99 2.99G 0.05414 0.1494 0.01349 0.217 226 640 0.02484 0.7607 0.07363 0.03289 0.04908 0.1321 0.01475 8 | 7/99 2.99G 0.05214 0.1493 0.01261 0.2141 215 640 0.02381 0.8454 0.08062 0.02338 0.05522 0.1065 0.01233 9 | 8/99 2.99G 0.05337 0.1471 0.01131 0.2118 202 640 0.03169 0.8981 0.09415 0.03889 0.04418 0.08979 0.01057 10 | 9/99 2.99G 0.05162 0.1508 0.01033 0.2127 286 640 0.02328 0.8735 0.0949 0.0396 0.05098 0.08382 0.01123 11 | 10/99 2.99G 0.05015 0.133 0.008946 0.1921 243 640 0.03059 0.9449 0.1147 0.05118 0.04166 0.08961 0.007056 12 | 11/99 2.99G 0.04833 0.1404 0.007613 0.1964 189 640 0.0287 0.9622 0.0867 0.03279 0.04503 0.1049 0.005475 13 | 12/99 2.99G 0.04835 0.1332 0.006345 0.1879 266 640 0.04288 0.9754 0.1183 0.05685 0.03615 0.09978 0.004429 14 | 13/99 2.99G 0.04842 0.139 0.005641 0.193 272 640 0.03229 0.9877 0.1173 0.05101 0.04139 0.1021 0.003933 15 | 14/99 2.99G 0.0483 0.1412 0.004905 0.1944 263 640 0.03611 0.9729 0.1025 0.04884 0.04228 0.1021 0.00359 16 | 15/99 2.99G 0.04597 0.1435 0.004331 0.1938 287 640 0.03563 0.986 0.1037 0.05422 0.0391 0.1113 0.003404 17 | 16/99 2.99G 0.04457 0.1394 0.004251 0.1882 329 640 0.03666 0.9795 0.1209 0.06275 0.03787 0.1148 0.003192 18 | 17/99 2.99G 0.04433 0.1414 0.003848 0.1895 283 640 0.04468 0.9704 0.1043 0.05619 0.03747 0.1046 0.003081 19 | 18/99 2.99G 0.04461 0.1378 0.003694 0.1861 190 640 0.06038 0.9359 0.1046 0.06095 0.0383 0.09606 0.003221 20 | 19/99 2.99G 0.04298 0.1365 0.003373 0.1828 241 640 0.05508 0.95 0.1159 0.05679 0.03973 0.08586 0.002872 21 | 20/99 2.99G 0.04183 0.1337 0.003377 0.179 234 640 0.05899 0.9647 0.1156 0.06299 0.03432 0.09205 0.003026 22 | 21/99 2.99G 0.04112 0.1423 0.003159 0.1866 295 640 0.05022 0.8751 0.09555 0.05051 0.04673 0.09976 0.004701 23 | 22/99 2.99G 0.04767 0.1368 0.003037 0.1875 226 640 0.05139 0.9556 0.09856 0.05655 0.03594 0.0955 0.002859 24 | 23/99 2.99G 0.04519 0.1293 0.003374 0.1778 323 640 0.03946 0.9688 0.1019 0.05831 0.04098 0.0944 0.003101 25 | 24/99 2.99G 0.04604 0.1335 0.003412 0.183 269 640 0.05287 0.8932 0.09809 0.05234 0.03795 0.08124 0.003449 26 | 25/99 2.99G 0.04453 0.1371 0.003306 0.185 236 640 0.07402 0.9433 0.1163 0.06754 0.0342 0.08428 0.002926 27 | 26/99 2.99G 0.04174 0.1405 0.003118 0.1854 292 640 0.05976 0.9787 0.1138 0.06027 0.03723 0.1096 0.002531 28 | 27/99 2.99G 0.04002 0.1343 0.00293 0.1773 293 640 0.0465 0.9631 0.1134 0.06123 0.03673 0.111 0.002507 29 | 28/99 2.99G 0.03931 0.1316 0.002832 0.1738 226 640 0.07434 0.9302 0.1127 0.05548 0.03794 0.08954 0.003052 30 | 29/99 2.99G 0.0405 0.1394 0.002977 0.1829 277 640 0.05824 0.9663 0.1109 0.06235 0.03057 0.09963 0.002554 31 | 30/99 2.99G 0.03686 0.1309 0.002669 0.1704 212 640 0.07319 0.9746 0.1256 0.07588 0.03011 0.1194 0.002223 32 | 31/99 2.99G 0.03735 0.131 0.002536 0.1709 191 640 0.07895 0.936 0.1095 0.06048 0.03131 0.08395 0.002445 33 | 32/99 2.99G 0.03831 0.125 0.002693 0.166 243 640 0.07102 0.9425 0.1136 0.06351 0.03722 0.1174 0.002401 34 | 33/99 2.99G 0.03816 0.1369 0.002527 0.1776 266 640 0.07542 0.9615 0.1189 0.0688 0.02877 0.1025 0.002274 35 | 34/99 2.99G 0.03518 0.1333 0.002303 0.1708 201 640 0.08689 0.9614 0.1269 0.07785 0.02958 0.1295 0.00226 36 | 35/99 2.99G 0.03401 0.1324 0.002306 0.1687 296 640 0.08803 0.9548 0.1204 0.07331 0.02727 0.1063 0.002285 37 | 36/99 2.99G 0.03953 0.1278 0.002334 0.1697 255 640 0.08475 0.9704 0.1279 0.07782 0.03053 0.1153 0.002447 38 | 37/99 2.99G 0.03796 0.1316 0.002418 0.1719 268 640 0.08322 0.7201 0.1032 0.06255 0.03468 0.08608 0.003271 39 | 38/99 2.99G 0.03847 0.1368 0.002244 0.1775 258 640 0.07656 0.8271 0.1001 0.06063 0.03498 0.1123 0.003657 40 | 39/99 2.99G 0.03758 0.1313 0.002395 0.1713 279 640 0.08076 0.9261 0.1114 0.0579 0.03574 0.09667 0.002623 41 | 40/99 2.99G 0.03753 0.1345 0.002396 0.1744 265 640 0.0896 0.8159 0.1049 0.06082 0.03175 0.08318 0.003636 42 | 41/99 2.99G 0.03423 0.1306 0.002162 0.167 230 640 0.08694 0.8487 0.1059 0.06107 0.03285 0.1206 0.002904 43 | 42/99 2.99G 0.0362 0.123 0.002077 0.1613 188 640 0.08975 0.9541 0.1376 0.08954 0.0282 0.08427 0.002081 44 | 43/99 2.99G 0.03441 0.1368 0.002161 0.1734 296 640 0.0799 0.982 0.1162 0.07123 0.02744 0.1305 0.002031 45 | 44/99 2.99G 0.03394 0.1293 0.002158 0.1654 233 640 0.09245 0.9343 0.1123 0.06853 0.02732 0.1086 0.002279 46 | 45/99 2.99G 0.03244 0.1296 0.001881 0.164 227 640 0.09489 0.9097 0.1098 0.0681 0.02947 0.1017 0.002066 47 | 46/99 2.99G 0.03063 0.1248 0.001725 0.1572 293 640 0.09663 0.9049 0.1136 0.07124 0.02907 0.09528 0.002208 48 | 47/99 2.99G 0.03185 0.1249 0.001731 0.1585 248 640 0.09642 0.8855 0.1064 0.06822 0.02727 0.114 0.002283 49 | 48/99 2.99G 0.03126 0.1282 0.00177 0.1612 421 640 0.09312 0.9336 0.1152 0.06624 0.03196 0.1027 0.00183 50 | 49/99 2.99G 0.03341 0.1243 0.001667 0.1593 325 640 0.08961 0.945 0.113 0.07084 0.02741 0.1134 0.001755 51 | 50/99 2.99G 0.03159 0.1224 0.001605 0.1556 226 640 0.09385 0.9318 0.1132 0.07366 0.02829 0.1144 0.002178 52 | 51/99 2.99G 0.02954 0.125 0.001688 0.1562 255 640 0.09386 0.926 0.1107 0.06942 0.02903 0.1267 0.002092 53 | 52/99 2.99G 0.03027 0.1219 0.00168 0.1538 287 640 0.08762 0.7746 0.08972 0.06003 0.02877 0.1132 0.002929 54 | 53/99 2.99G 0.0275 0.1187 0.001544 0.1477 307 640 0.09202 0.8917 0.1082 0.067 0.03041 0.1028 0.001897 55 | 54/99 2.99G 0.02816 0.1202 0.001425 0.1498 322 640 0.09091 0.8168 0.1017 0.06588 0.02768 0.1031 0.002009 56 | 55/99 2.99G 0.02939 0.1229 0.001264 0.1535 252 640 0.09527 0.8728 0.1184 0.07646 0.02808 0.1079 0.001864 57 | 56/99 2.99G 0.0285 0.1212 0.001348 0.151 283 640 0.09368 0.8153 0.1062 0.06946 0.02854 0.1047 0.002162 58 | 57/99 2.99G 0.02701 0.1194 0.001327 0.1478 308 640 0.09217 0.8112 0.09713 0.06422 0.0264 0.09917 0.001998 59 | 58/99 2.99G 0.02801 0.1171 0.001263 0.1463 299 640 0.09239 0.9105 0.1042 0.07091 0.02495 0.1316 0.001667 60 | 59/99 2.99G 0.02856 0.1153 0.001152 0.145 202 640 0.09259 0.9082 0.105 0.06905 0.02885 0.1124 0.001763 61 | 60/99 2.99G 0.02683 0.1108 0.001136 0.1388 273 640 0.09009 0.7528 0.09182 0.06093 0.02828 0.1011 0.002107 62 | 61/99 2.99G 0.02601 0.1166 0.001195 0.1438 259 640 0.09562 0.9007 0.09917 0.06464 0.02696 0.1287 0.001674 63 | 62/99 2.99G 0.0259 0.1114 0.001083 0.1384 229 640 0.09262 0.8958 0.09674 0.06258 0.02592 0.1386 0.001604 64 | 63/99 2.99G 0.02662 0.116 0.000923 0.1436 298 640 0.0904 0.8383 0.09598 0.0619 0.02604 0.1252 0.001694 65 | 64/99 2.99G 0.02671 0.1112 0.001027 0.1389 215 640 0.09143 0.8399 0.09554 0.06265 0.02794 0.1286 0.001682 66 | 65/99 2.99G 0.02633 0.1102 0.0009638 0.1375 279 640 0.09123 0.8917 0.09746 0.06554 0.02593 0.1416 0.001527 67 | 66/99 2.99G 0.02585 0.1087 0.0009864 0.1356 284 640 0.08475 0.7263 0.09015 0.06037 0.02906 0.124 0.002057 68 | 67/99 2.99G 0.02576 0.1112 0.0009898 0.1379 378 640 0.09044 0.8334 0.09253 0.06211 0.02691 0.1308 0.00204 69 | 68/99 2.99G 0.02624 0.1114 0.001051 0.1386 254 640 0.09118 0.8573 0.09163 0.06047 0.02626 0.1328 0.001776 70 | 69/99 2.99G 0.02512 0.1133 0.001031 0.1394 274 640 0.08945 0.8112 0.08919 0.05878 0.02713 0.1311 0.002067 71 | 70/99 2.99G 0.02551 0.1092 0.001002 0.1357 244 640 0.08981 0.8135 0.08828 0.05868 0.02684 0.1355 0.002004 72 | 71/99 2.99G 0.02478 0.1045 0.0009794 0.1303 210 640 0.08969 0.7989 0.0871 0.05654 0.02912 0.1176 0.002029 73 | 72/99 2.99G 0.02472 0.1097 0.0009363 0.1353 319 640 0.0874 0.7865 0.08485 0.05583 0.02716 0.1419 0.001895 74 | 73/99 2.99G 0.02382 0.1041 0.0008868 0.1288 289 640 0.08317 0.7167 0.07827 0.05174 0.02944 0.1337 0.002343 75 | 74/99 2.99G 0.02415 0.1068 0.0006749 0.1316 232 640 0.08588 0.7873 0.08678 0.05618 0.02762 0.144 0.001776 76 | 75/99 2.99G 0.02543 0.1123 0.0008221 0.1386 287 640 0.08498 0.7628 0.08489 0.05565 0.02784 0.1311 0.002143 77 | 76/99 2.99G 0.02449 0.1086 0.0007149 0.1338 301 640 0.08021 0.7258 0.07638 0.05115 0.0281 0.1375 0.002017 78 | 77/99 2.99G 0.02445 0.1079 0.0007195 0.133 246 640 0.08477 0.7882 0.08108 0.05423 0.02733 0.1512 0.001915 79 | 78/99 2.99G 0.02469 0.1051 0.0008169 0.1306 303 640 0.08707 0.79 0.08268 0.05354 0.02803 0.1394 0.00228 80 | 79/99 2.99G 0.02421 0.1031 0.0007279 0.128 262 640 0.08452 0.7694 0.07959 0.05192 0.0288 0.1412 0.002402 81 | 80/99 2.99G 0.02369 0.101 0.000777 0.1254 258 640 0.08123 0.6944 0.07483 0.0495 0.02999 0.1334 0.002776 82 | 81/99 2.99G 0.02367 0.1086 0.0007513 0.133 218 640 0.08611 0.8006 0.08306 0.05574 0.02715 0.152 0.001868 83 | 82/99 2.99G 0.02349 0.1025 0.0007333 0.1268 205 640 0.08558 0.789 0.0834 0.0545 0.02837 0.1408 0.001761 84 | 83/99 2.99G 0.023 0.102 0.0007582 0.1257 348 640 0.08388 0.7816 0.08153 0.0548 0.0274 0.1537 0.001802 85 | 84/99 2.99G 0.02306 0.1014 0.0007739 0.1252 230 640 0.08222 0.7159 0.07493 0.05063 0.02813 0.1498 0.00219 86 | 85/99 2.99G 0.02337 0.1012 0.0007059 0.1253 302 640 0.08488 0.758 0.08163 0.05371 0.02757 0.1537 0.002222 87 | 86/99 2.99G 0.02303 0.1034 0.0006515 0.1271 301 640 0.08023 0.7349 0.07557 0.04884 0.02753 0.1615 0.002083 88 | 87/99 2.99G 0.02356 0.09942 0.0006224 0.1236 277 640 0.08039 0.7356 0.07642 0.05071 0.02731 0.1598 0.001946 89 | 88/99 2.99G 0.02298 0.1042 0.0007346 0.1279 258 640 0.08027 0.7186 0.07407 0.04897 0.02721 0.1558 0.002085 90 | 89/99 2.99G 0.02306 0.1027 0.0006272 0.1263 342 640 0.07911 0.7078 0.07182 0.04723 0.02817 0.1587 0.002227 91 | 90/99 2.99G 0.0232 0.09996 0.0006278 0.1238 257 640 0.07416 0.6659 0.06801 0.04603 0.02758 0.1541 0.00201 92 | 91/99 2.99G 0.02272 0.09907 0.0006487 0.1224 202 640 0.07258 0.6339 0.06915 0.04524 0.02852 0.1569 0.002372 93 | 92/99 2.99G 0.02267 0.1006 0.0005043 0.1238 210 640 0.07017 0.61 0.06219 0.04123 0.02972 0.1581 0.002632 94 | 93/99 2.99G 0.02291 0.09817 0.0006326 0.1217 192 640 0.0749 0.6617 0.06952 0.04637 0.02964 0.1587 0.002649 95 | 94/99 2.99G 0.02276 0.09796 0.0006182 0.1213 224 640 0.07381 0.6472 0.06758 0.04397 0.02911 0.1636 0.002738 96 | 95/99 2.99G 0.02238 0.09964 0.0006515 0.1227 273 640 0.07534 0.6641 0.0697 0.04595 0.02913 0.1695 0.002657 97 | 96/99 2.99G 0.02279 0.09839 0.0007068 0.1219 282 640 0.07762 0.6913 0.06852 0.0449 0.02869 0.1646 0.002219 98 | 97/99 2.99G 0.02284 0.09773 0.000584 0.1212 212 640 0.0726 0.6246 0.06654 0.04358 0.02948 0.1618 0.002799 99 | 98/99 2.99G 0.02223 0.09685 0.0005534 0.1196 287 640 0.07513 0.6464 0.06758 0.04507 0.03028 0.1661 0.003176 100 | 99/99 2.99G 0.023 0.1017 0.0006363 0.1253 282 640 0.07779 0.7079 0.07313 0.04838 0.02859 0.1734 0.002374 101 | -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/test_batch0_gt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/test_batch0_gt.jpg -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/test_batch0_pred.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/test_batch0_pred.jpg -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/train_batch0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/train_batch0.jpg -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/train_batch1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/train_batch1.jpg -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/train_batch2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/train_batch2.jpg -------------------------------------------------------------------------------- /Blood-Detector/runs/FINAL/weights/best.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/FINAL/weights/best.pt -------------------------------------------------------------------------------- /Blood-Detector/runs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/runs/__init__.py -------------------------------------------------------------------------------- /Blood-Detector/train.py: -------------------------------------------------------------------------------- 1 | #exec(open("test.py --name prueba").read()) 2 | import os 3 | 4 | def trainModel(): 5 | 6 | os.system('python ./../YOLOv5/train.py --img 640 --batch 16 --epochs 100 --hyp ./../YOLOv5/data/hyp.scratch.yaml --data ./../Dataset/bloodData.yaml --cfg ./../YOLOv5/models/yolov5s.yaml --weights ./../YOLOv5/models/yolov5s.pt --cache-images --name FINAL') 7 | 8 | if __name__ == '__main__': 9 | trainModel() 10 | 11 | # 100 epochs completed in 0.876 hours. -------------------------------------------------------------------------------- /Blood-Detector/web/index.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, redirect, url_for, send_from_directory 2 | from werkzeug.utils import secure_filename 3 | import os 4 | import sys 5 | import time 6 | 7 | path_blood_det_py = "./../" 8 | sys.path.append(path_blood_det_py) 9 | from blood_detection import detection 10 | 11 | app = Flask(__name__) 12 | 13 | UPLOAD_FOLDER = os.path.abspath("./static/inference/input") 14 | OUTPUT_FOLDER = os.path.abspath("./static/inference/output") 15 | app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER 16 | app.config["OUTPUT_FOLDER"] = OUTPUT_FOLDER 17 | ALLOWED_EXTENSIONS = set(["mp4", "jpg", "png", "jpeg"]) 18 | 19 | def allowed_file(filename): 20 | 21 | return "." in filename and filename.rsplit(".", 1)[1] in ALLOWED_EXTENSIONS 22 | 23 | 24 | fileNameInput = "" 25 | boolI = False 26 | boolV = False 27 | finished = False 28 | time_total, counter = 0, 0 29 | @app.route("/", methods = ["GET", "POST"]) 30 | def home(): 31 | 32 | global fileNameInput, boolI, boolV, time_total, finished, counter 33 | filename = fileNameInput 34 | boolImage = boolI 35 | boolVideo = boolV 36 | t_t = time_total 37 | list_files_out = os.listdir(app.config["OUTPUT_FOLDER"]) 38 | finished= True if counter != 0 and filename in list_files_out else False 39 | 40 | if request.method == "POST": 41 | 42 | 43 | if "ourfile" in request.files: 44 | f = request.files["ourfile"] 45 | 46 | if f.filename == "": 47 | return "No se ha seleccionado ningún archivo NADA" 48 | 49 | if f and allowed_file(f.filename): 50 | filename = secure_filename(f.filename) 51 | filenameAbsolute = os.path.join(app.config["UPLOAD_FOLDER"], filename) 52 | f.save(filenameAbsolute) 53 | fileNameInput = filename 54 | 55 | boolI = True if filename.split(".")[-1] in ("jpg", "png", "jpeg") else False 56 | boolImage = boolI 57 | boolV = True if filename.split(".")[-1] in ("mp4") else False 58 | boolVideo = boolV 59 | 60 | # ----------------------------------------------------------------------------------- 61 | # Predict model 62 | 63 | if "predictRequest" in request.form: 64 | 65 | start = time.time() 66 | fnsd = detection(filename) 67 | done = time.time() 68 | 69 | elapsed = done - start 70 | if fnsd == 0: 71 | counter += 1 72 | finished, time_total = True, round(float(str(elapsed).split(" ")[0]), 3) 73 | else: 74 | finished, time_total = False, round(float(str(elapsed).split(" ")[0]), 3) 75 | #finished = False 76 | 77 | #------------------------------------------------------------------------------------- 78 | return redirect("/#about") 79 | 80 | # Directory input and output cleaned 81 | 82 | files_uploaded = [f_ for f_ in os.listdir(app.config["UPLOAD_FOLDER"])] 83 | if len(files_uploaded) > 20: 84 | for file in files_uploaded: 85 | if file not in ('test_video.mp4', 'test_image.jpg'): 86 | os.remove(os.path.join(app.config["UPLOAD_FOLDER"], file)) 87 | print("") 88 | print("Directory input cleaned!") 89 | print("") 90 | 91 | files_predicted = [f_ for f_ in os.listdir(app.config["OUTPUT_FOLDER"])] 92 | 93 | if len(files_predicted) > 22: 94 | for file in files_predicted: 95 | if file not in ('test_video.mp4', 'test_image.jpg'): 96 | os.remove(os.path.join(app.config["OUTPUT_FOLDER"], file)) 97 | print("") 98 | print("Directory output cleaned!") 99 | print("") 100 | 101 | #------------------------------------------------------------------------------------- 102 | 103 | context = {'mediaFileName': filename, 'boolVideo': boolVideo, 104 | 'boolImage': boolImage, 'time': t_t, 'finished': finished} 105 | 106 | return render_template("index.html", context = context) 107 | 108 | if __name__ == '__main__': 109 | app.run(debug=True, host='0.0.0.0', port=5000, threaded=True) -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/README/brandlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/README/brandlogo.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/blog/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/blog/2.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/blog/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/blog/3.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/blog/alexander.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/blog/alexander.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/hero/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/hero/1.jpg -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/hero/1_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/hero/1_1.jpg -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/hero/final_mobile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/hero/final_mobile.gif -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/hero/final_pc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/hero/final_pc.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/hero/github_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/hero/github_logo.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/hero/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/hero/logo.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/hero/paper_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/hero/paper_logo.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/hero/text_enigma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/hero/text_enigma.png -------------------------------------------------------------------------------- /Blood-Detector/web/static/images/skill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/images/skill.jpg -------------------------------------------------------------------------------- /Blood-Detector/web/static/inference/input/test_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/inference/input/test_image.jpg -------------------------------------------------------------------------------- /Blood-Detector/web/static/inference/input/test_video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/inference/input/test_video.mp4 -------------------------------------------------------------------------------- /Blood-Detector/web/static/inference/output/test_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Blood-Detector/web/static/inference/output/test_image.jpg -------------------------------------------------------------------------------- /Blood-Detector/web/static/js/script.js: -------------------------------------------------------------------------------- 1 | // Fijando la cabecera 2 | 3 | window.onscroll=function(){ 4 | const docScrollTop=document.documentElement.scrollTop; 5 | 6 | if(window.innerWidth>991){ 7 | if(docScrollTop>100){ 8 | document.querySelector("header").classList.add("fixed") 9 | } 10 | else{ 11 | document.querySelector("header").classList.remove("fixed") 12 | } 13 | } 14 | } 15 | 16 | // navbar links 17 | 18 | const navbar = document.querySelector(".navbar"); 19 | a=navbar.querySelectorAll("a"); 20 | 21 | a.forEach(function(element){ 22 | element.addEventListener("click", function(){ 23 | for(let i=0; i 2 | 3 | 4 | 5 | 6 | ENIGMA PROJECT 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ENIGMA PROJECT 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 |
26 |
27 | Responsive image 28 |
29 | 30 |
31 | ENIGMA A.I. 32 |
33 | 34 |
35 | 36 |
37 | 44 |
45 |
46 |
47 | 48 | 49 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |

BLOOD DETECTOR

58 |
59 |
60 |
61 | Currently, many rural areas of Peru still do not have a specialized medical center to perform different blood tests. Many of the equipment necessary for this purpose require a high economic investment and qualified personnel. This work proposes a fast and inexpensive system for the recognition of 3 types of blood cells based on convolutional networks. 62 |
63 | 64 |
65 | Github 66 | Paper 67 |
68 | 69 |
70 |
71 |
72 |
73 | 74 | 75 | 76 |
77 |
78 |
79 |
80 |

Run Model

81 |
82 |
83 | The base model is YOLOv5 built on Pytorch, running on a RaspBerry Pi 4. 84 |
85 |
86 |
87 |
88 |
89 |

90 | Upload an image or video: 91 |

92 | 93 | 94 | 95 |
96 |
97 |
98 |
99 |

100 | Make inference: 101 |

102 | 103 | 104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | {% if context %} 114 | 115 | {% if context["boolImage"] == True %} 116 |
117 | 118 |
119 | {% endif %} 120 | 121 | {% if context["boolVideo"] == True %} 122 |
123 | 126 |
127 | {% endif %} 128 | 129 | {% if context["mediaFileName"] %} 130 |
131 | Chosen file: 132 |

{{context["mediaFileName"]}}

133 |
134 | {% else %} 135 |

Valid formats: "mp4", "jpg", "png", "jpeg"

136 | {% endif %} 137 |
138 | {% endif %} 139 |
140 |
141 | {% if context["finished"] == True %} 142 | 143 | {% if context["boolImage"] == True %} 144 |
145 | 146 |
147 | {% endif %} 148 | 149 | {% if context["boolVideo"] == True %} 150 |
151 | 154 |
155 | {% endif %} 156 | 157 | {% if context["time"] %} 158 |
159 | Time inference: 160 |

{{context["time"]}} seconds

161 |
162 | {% endif %} 163 |
164 | {% else %} 165 |
166 |

Wait Please

167 |
168 | {% endif %} 169 |
170 |
171 |
172 | 173 |
174 |
175 |
176 | 177 | 178 | 179 |
180 |
181 |
182 |
183 |
184 |
185 |

ABOUT THE DEPLOYMENT

186 |

In the deployment of the model not only use YOLOv5, we used other tools, as shown below.

187 |
188 |
189 |
190 |
191 |
Pytorch
192 |
193 |
194 | 195 | 70% 196 | 197 |
198 |
199 |
200 |
201 |
OpenCV
202 |
203 |
204 | 205 | 50% 206 | 207 |
208 |
209 |
210 |
211 |
Flask
212 |
213 |
214 | 215 | 65% 216 | 217 |
218 |
219 |
220 |
221 |
Tensorboad
222 |
223 |
224 | 225 | 30% 226 | 227 |
228 |
229 |
230 |
231 |
Streamlit
232 |
233 |
234 | 235 | 25% 236 | 237 |
238 |
239 |
240 |
241 |
242 |
243 | skill 244 |
245 |
246 |
247 |
248 | 249 | 250 | 251 | 252 |
253 |
254 |
255 |
256 |

If you are passionate about Artificial Intelligence, and you plan to give your best in future projects, contact us.

257 |
258 |
259 | Get In Touch 260 | 261 |
262 |
263 |
264 | 265 |
266 | 267 | 268 | 269 |
270 |
271 |
272 |
273 |

About Us

274 |
275 |
276 |
277 |
278 |
279 |
280 | blog 281 |
282 |
283 | Computer Science - UTEC 284 |
285 |

286 | Alexander Morales Panitz 287 |

288 |

Founder of Enigma A.I. and QuantumHispano. Self-taught student in Quantum Machine Learning.

289 | Read More 290 |
291 |
292 |
293 | blog 294 |
295 |
296 | Computer Science - 297 | University National of Engineering 298 | 299 |
300 |

301 | Cristhian Wiki Sánchez Sauñe 302 |

303 |

Deep Learning Practitioner, and research assistant at UNI. Experience in managing frameworks and deploying models (with Java, C++, Python or Javascript).

304 | Read More 305 |
306 |
307 |
308 | blog 309 |
310 |
311 | Electronic Engineering - UNAC 312 |
313 |

314 | César Atusparia Alhua 315 |

316 |

Electronics student interested in the analysis of medical images in the area of ​​DeepLearning

317 | Read More 318 |
319 |
320 |
321 |
322 | 323 |
324 | 325 | 326 | 327 | 340 | 341 | 342 | 343 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | -------------------------------------------------------------------------------- /Dataset/BCCD/Annotations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Dataset/BCCD/Annotations/__init__.py -------------------------------------------------------------------------------- /Dataset/BCCD/JPEGImages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Dataset/BCCD/JPEGImages/__init__.py -------------------------------------------------------------------------------- /Dataset/BCCD/ModelData/images/train/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Dataset/BCCD/ModelData/images/train/__init__.py -------------------------------------------------------------------------------- /Dataset/BCCD/ModelData/images/val/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Dataset/BCCD/ModelData/images/val/__init__.py -------------------------------------------------------------------------------- /Dataset/BCCD/ModelData/labels/train.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Dataset/BCCD/ModelData/labels/train.cache -------------------------------------------------------------------------------- /Dataset/BCCD/ModelData/labels/train/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Dataset/BCCD/ModelData/labels/train/__init__.py -------------------------------------------------------------------------------- /Dataset/BCCD/ModelData/labels/val.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Dataset/BCCD/ModelData/labels/val.cache -------------------------------------------------------------------------------- /Dataset/BCCD/ModelData/labels/val/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/Dataset/BCCD/ModelData/labels/val/__init__.py -------------------------------------------------------------------------------- /Dataset/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 shenggan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Dataset/bloodData.yaml: -------------------------------------------------------------------------------- 1 | # train and val data 2 | train: ./../Dataset/BCCD/ModelData/images/train/ 3 | val: ./../Dataset/BCCD/ModelData/images/val/ 4 | 5 | # number of classes 6 | nc: 3 7 | 8 | # class names 9 | names: ['RBC', 'WBC', 'Platelet'] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Enigma A.I. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 |

Blood Cells Detection System for rural zones at Peru 🔬🩸

5 |

6 | 7 |

8 |

9 | 10 | 11 | 12 | 13 | 14 |

15 |

16 | 17 | 18 | ## 📜 Abstract 19 | Currently, many rural areas of Peru still do not have a specialized medical center to perform different blood tests. Many of the equipment necessary for this purpose require a high economic investment and qualified personnel. This work proposes a fast and inexpensive system for the recognition of 3 types of blood cells based on convolutional networks. The ConvNet model is characterized by having reduced inference times, and also ease of deployment in hardware with reduced resources such as a Raspberry Pi. 20 | 21 | The paper is available at this [link](https://drive.google.com/file/d/1FEv4wR2A_vQybh7ARqoT6nD2xu_SC8T3/view?usp=sharing). 22 | 23 | ## 🆕 Update 24 | - 29/11/20: Improved and more personalized interface. Discontinued use 25 | **Streamlit** due to the limitation in the customization layer; it was replaced with native CSS and HTML, rendered by **Flask**. Improved file management system, now allows files to be stored on the server, using FLASK's own security protocols. 26 | 27 | TODO: 28 | - ✅ File storage directly on the server with Flask 29 | - ✅ Responsive web design 30 | - ⬜️ Support for React and Django 31 | - ⬜️ Model quantization 32 | - ⬜️ Support for AWS and Docker 33 | 34 | ## 📖 Content 35 | The following tree shows the structure of the application: 36 | ``` 37 | |- master-RasPi-BloodView/ 38 | | |- Blood-Detector/ 39 | | |- runs/ 40 | | |- FINAL/ 41 | | |- weights/ 42 | | |- best.pt 43 | | |- other torchscript files .. 44 | | |- .. 45 | | |- web/ 46 | | |- static/.. 47 | | |- template/.. 48 | | |- index.py 49 | | |- blood_detection.py 50 | | |- create_csv.py 51 | | |- data_viz.py 52 | | |- data_manage.py 53 | | |- export_jit.py 54 | | |- train.py 55 | | 56 | | |- Dataset/ 57 | | |- BCCD/ 58 | | |- Annotations/*.xml 59 | | |- JPEGImages/*.jpg 60 | | |- ModelData/ 61 | | |- images/ 62 | | |- train/*.jpg 63 | | |- val/*.jpg 64 | | |- labels/ 65 | | |- train/*.txt 66 | | |- test/*.txt 67 | | |- annotations_blood_cells.csv 68 | | |- bloodData.yaml and LICENSE 69 | | 70 | | |- YOLOv5/ 71 | | |- data/.. 72 | | |- models/.. 73 | | |- utils/.. 74 | | |- detect.py (modificated) 75 | | |- train.py 76 | | |- test.py 77 | | |- LICENSE 78 | | 79 | | |- README.md 80 | ``` 81 | ## ℹ️ Instructions 82 | 83 | Install all dependencies with the command ```pip install -r requirements.txt```. To install Pytorch ARM on the RaspBerry, you need to compile it by following these [instructions](https://mathinf.eu/pytorch/arm64/). 84 | Similarly, you need to compile OpenCV by following this [tutorial](https://qengineering.eu/install-opencv-4.2-on-raspberry-pi-4.html) (it will take about 5 hours). 85 | 86 | Pytorch and OpenCV are not officially available for RaspBerry at the time of publication of this work (11/30/2020). 87 | 88 | * Note: All the commands described below are executed in folder Blood-Detector. 89 | 90 | 1. Data collection and pre-processing 91 | Original Dataset available at [here](https://github.com/Shenggan/BCCD_Dataset). 92 | Place the data following the tree structure shown above. Run the following command to process the .xml files and get a .csv file with the coordinates of each blood cell (bounding box and centroid axis) per image. 93 | ``` 94 | python create_csv.py 95 | ``` 96 | 97 | If you want to display the previously labeled bounding boxes, you can run the following command. 98 | ``` 99 | python data_viz.py 100 | ``` 101 | 102 | Finally we need to split our data into a validation and training set. The following command generates .txt files for the tags and also copies the images to the 'train' and 'val' folders. 103 | ``` 104 | python data_manage.py 105 | ``` 106 | 107 | 2. Train and export model 108 | 109 | * Note: If you have other data, modify the .yaml file inside the Dataset folder. 110 | 111 | To train the model, just run the following command (you can modify this file to change the hyperparameters). 112 | Training for 100 epochs (on an Nvidia 1050Ti graphics card) took about 1 hour. 113 | ``` 114 | python train.py 115 | ``` 116 | The model is saved in the 'runs' folder. 117 | 118 | To export the model in JIT format you need to run the following command. 119 | ``` 120 | python export_jit.py 121 | ``` 122 | The generated file will be saved in the same folder 'runs'. 123 | 124 | 3. Test model 125 | 126 | To run the application, inside the 'web' folder execute the following command. 127 | ``` 128 | python index.py 129 | ``` 130 | 131 | Below is a screenshot from PC and mobile. 132 | 133 |

134 |
135 | 136 |

PC

137 |
138 |

139 | 140 |

141 | 142 |

Mobile

143 |

144 | 145 | 146 | ## 👨‍💻 Maintainers 147 | * Cristhian Wiki, Github: [HiroForYou](https://github.com/HiroForYou) Email: csanchezs@uni.pe 148 | 149 | ## 🙏🏽 Special thanks 150 | * Version 1.5: 151 | Many thanks to the members of ENIGMA-AI (Cesar and Alexander) for the commitment presented in the project for almost two months. 152 | This work would not have been possible without team support. 153 | * Version 2: 154 | *Soon* -------------------------------------------------------------------------------- /YOLOv5/data/coco.yaml: -------------------------------------------------------------------------------- 1 | # COCO 2017 dataset http://cocodataset.org 2 | # Train command: python train.py --data coco.yaml 3 | # Default dataset location is next to /yolov5: 4 | # /parent_folder 5 | # /coco 6 | # /yolov5 7 | 8 | 9 | # download command/URL (optional) 10 | download: bash data/scripts/get_coco.sh 11 | 12 | # train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/] 13 | train: ../coco/train2017.txt # 118287 images 14 | val: ../coco/val2017.txt # 5000 images 15 | test: ../coco/test-dev2017.txt # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794 16 | 17 | # number of classes 18 | nc: 80 19 | 20 | # class names 21 | names: ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 22 | 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 23 | 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 24 | 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 25 | 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 26 | 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 27 | 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 28 | 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 29 | 'hair drier', 'toothbrush'] 30 | 31 | # Print classes 32 | # with open('data/coco.yaml') as f: 33 | # d = yaml.load(f, Loader=yaml.FullLoader) # dict 34 | # for i, x in enumerate(d['names']): 35 | # print(i, x) 36 | -------------------------------------------------------------------------------- /YOLOv5/data/coco128.yaml: -------------------------------------------------------------------------------- 1 | # COCO 2017 dataset http://cocodataset.org - first 128 training images 2 | # Train command: python train.py --data coco128.yaml 3 | # Default dataset location is next to /yolov5: 4 | # /parent_folder 5 | # /coco128 6 | # /yolov5 7 | 8 | 9 | # download command/URL (optional) 10 | download: https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip 11 | 12 | # train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/] 13 | train: ../coco128/images/train2017/ # 128 images 14 | val: ../coco128/images/train2017/ # 128 images 15 | 16 | # number of classes 17 | nc: 80 18 | 19 | # class names 20 | names: ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 21 | 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 22 | 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 23 | 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 24 | 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 25 | 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 26 | 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 27 | 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 28 | 'hair drier', 'toothbrush'] 29 | -------------------------------------------------------------------------------- /YOLOv5/data/hyp.finetune.yaml: -------------------------------------------------------------------------------- 1 | # Hyperparameters for VOC finetuning 2 | # python train.py --batch 64 --weights yolov5m.pt --data voc.yaml --img 512 --epochs 50 3 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 4 | 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 | -------------------------------------------------------------------------------- /YOLOv5/data/hyp.scratch.yaml: -------------------------------------------------------------------------------- 1 | # Hyperparameters for COCO training from scratch 2 | # python train.py --batch 40 --cfg yolov5m.yaml --weights '' --data coco.yaml --img 640 --epochs 300 3 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 4 | 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.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: 0 # anchors per output grid (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 | -------------------------------------------------------------------------------- /YOLOv5/data/scripts/get_coco.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # COCO 2017 dataset http://cocodataset.org 3 | # Download command: bash data/scripts/get_coco.sh 4 | # Train command: python train.py --data coco.yaml 5 | # Default dataset location is next to /yolov5: 6 | # /parent_folder 7 | # /coco 8 | # /yolov5 9 | 10 | # Download/unzip labels 11 | d='../' # unzip directory 12 | url=https://github.com/ultralytics/yolov5/releases/download/v1.0/ 13 | f='coco2017labels.zip' # 68 MB 14 | echo 'Downloading' $url$f ' ...' && curl -L $url$f -o $f && unzip -q $f -d $d && rm $f # download, unzip, remove 15 | 16 | # Download/unzip images 17 | d='../coco/images' # unzip directory 18 | url=http://images.cocodataset.org/zips/ 19 | f1='train2017.zip' # 19G, 118k images 20 | f2='val2017.zip' # 1G, 5k images 21 | f3='test2017.zip' # 7G, 41k images (optional) 22 | for f in $f1 $f2; do 23 | echo 'Downloading' $url$f ' ...' && curl -L $url$f -o $f && unzip -q $f -d $d && rm $f # download, unzip, remove 24 | done 25 | -------------------------------------------------------------------------------- /YOLOv5/data/scripts/get_voc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC/ 3 | # Download command: bash data/scripts/get_voc.sh 4 | # Train command: python train.py --data voc.yaml 5 | # Default dataset location is next to /yolov5: 6 | # /parent_folder 7 | # /VOC 8 | # /yolov5 9 | 10 | start=$(date +%s) 11 | mkdir -p ../tmp 12 | cd ../tmp/ 13 | 14 | # Download/unzip images and labels 15 | d='.' # unzip directory 16 | url=https://github.com/ultralytics/yolov5/releases/download/v1.0/ 17 | f1=VOCtrainval_06-Nov-2007.zip # 446MB, 5012 images 18 | f2=VOCtest_06-Nov-2007.zip # 438MB, 4953 images 19 | f3=VOCtrainval_11-May-2012.zip # 1.95GB, 17126 images 20 | for f in $f1 $f2 $f3; do 21 | echo 'Downloading' $url$f ' ...' && curl -L $url$f -o $f && unzip -q $f -d $d && rm $f # download, unzip, remove 22 | done 23 | 24 | end=$(date +%s) 25 | runtime=$((end - start)) 26 | echo "Completed in" $runtime "seconds" 27 | 28 | echo "Spliting dataset..." 29 | python3 - "$@" <train.txt 89 | cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt >train.all.txt 90 | 91 | python3 - "$@" <= 1 99 | p, s, im0 = path[i], '%g: ' % i, im0s[i].copy() 100 | else: 101 | p, s, im0 = path, '', im0s 102 | 103 | save_path = str(Path(out) / Path(p).name) 104 | txt_path = str(Path(out) / Path(p).stem) + ('_%g' % dataset.frame if dataset.mode == 'video' else '') 105 | s += '%gx%g ' % img.shape[2:] # print string 106 | gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh 107 | if det is not None and len(det): 108 | # Rescale boxes from img_size to im0 size 109 | det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round() 110 | 111 | # Print results 112 | for c in det[:, -1].unique(): 113 | n = (det[:, -1] == c).sum() # detections per class 114 | s += '%g %ss, ' % (n, names[int(c)]) # add to string 115 | 116 | # Write results 117 | for *xyxy, conf, cls in reversed(det): 118 | if save_txt: # Write to file 119 | xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh 120 | with open(txt_path + '.txt', 'a') as f: 121 | f.write(('%g ' * 5 + '\n') % (cls, *xywh)) # label format 122 | 123 | if save_img or view_img: # Add bbox to image 124 | label = '%s %.2f' % (names[int(cls)], conf) 125 | plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3) 126 | 127 | # Print time (inference + NMS) 128 | print('%sDone. (%.3fs)' % (s, t2 - t1)) 129 | 130 | # Stream results 131 | if view_img: 132 | cv2.imshow(p, im0) 133 | if cv2.waitKey(1) == ord('q'): # q to quit 134 | raise StopIteration 135 | 136 | # Save results (image with detections) 137 | if save_img: 138 | if dataset.mode == 'images': 139 | cv2.imwrite(save_path, im0) 140 | else: 141 | if vid_path != save_path: # new video 142 | vid_path = save_path 143 | if isinstance(vid_writer, cv2.VideoWriter): 144 | vid_writer.release() # release previous video writer 145 | 146 | #fourcc = 'mp4v' # output video codec 147 | fourcc = 'H264' # output video codec 148 | fps = vid_cap.get(cv2.CAP_PROP_FPS) 149 | w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH)) 150 | h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) 151 | vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*fourcc), fps, (w, h)) 152 | #out = cv2.VideoWriter_fourcc((*'X264') 153 | vid_writer.write(im0) 154 | 155 | if save_txt or save_img: 156 | print('Results saved to %s' % Path(out)) 157 | 158 | time_total = time.time() - t0 159 | print('Done. (%.3fs)' % (time_total)) 160 | print('\n\n') 161 | 162 | 163 | if __name__ == '__main__': 164 | parser = argparse.ArgumentParser() 165 | parser.add_argument('--weights', nargs='+', type=str, default='yolov5s.pt', help='model.pt path(s)') 166 | parser.add_argument('--source', type=str, default='inference/images', help='source') # file/folder, 0 for webcam 167 | parser.add_argument('--output', type=str, default='inference/output', help='output folder') # output folder 168 | parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)') 169 | parser.add_argument('--conf-thres', type=float, default=0.4, help='object confidence threshold') 170 | parser.add_argument('--iou-thres', type=float, default=0.5, help='IOU threshold for NMS') 171 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 172 | parser.add_argument('--view-img', action='store_true', help='display results') 173 | parser.add_argument('--save-txt', action='store_true', help='save results to *.txt') 174 | parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3') 175 | parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS') 176 | parser.add_argument('--augment', action='store_true', help='augmented inference') 177 | parser.add_argument('--update', action='store_true', help='update all models') 178 | opt = parser.parse_args() 179 | print(opt) 180 | 181 | with torch.no_grad(): 182 | if opt.update: # update all models (to fix SourceChangeWarning) 183 | for opt.weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt']: 184 | detect() 185 | strip_optimizer(opt.weights) 186 | else: 187 | detect() 188 | -------------------------------------------------------------------------------- /YOLOv5/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/YOLOv5/models/__init__.py -------------------------------------------------------------------------------- /YOLOv5/models/common.py: -------------------------------------------------------------------------------- 1 | # This file contains modules common to various models 2 | 3 | import math 4 | import numpy as np 5 | import torch 6 | import torch.nn as nn 7 | 8 | from utils.datasets import letterbox 9 | from utils.general import non_max_suppression, make_divisible, scale_coords 10 | 11 | 12 | def autopad(k, p=None): # kernel, padding 13 | # Pad to 'same' 14 | if p is None: 15 | p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad 16 | return p 17 | 18 | 19 | def DWConv(c1, c2, k=1, s=1, act=True): 20 | # Depthwise convolution 21 | return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act) 22 | 23 | 24 | class Conv(nn.Module): 25 | # Standard convolution 26 | def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups 27 | super(Conv, self).__init__() 28 | self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) 29 | self.bn = nn.BatchNorm2d(c2) 30 | self.act = nn.Hardswish() if act else nn.Identity() 31 | 32 | def forward(self, x): 33 | return self.act(self.bn(self.conv(x))) 34 | 35 | def fuseforward(self, x): 36 | return self.act(self.conv(x)) 37 | 38 | 39 | class Bottleneck(nn.Module): 40 | # Standard bottleneck 41 | def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion 42 | super(Bottleneck, self).__init__() 43 | c_ = int(c2 * e) # hidden channels 44 | self.cv1 = Conv(c1, c_, 1, 1) 45 | self.cv2 = Conv(c_, c2, 3, 1, g=g) 46 | self.add = shortcut and c1 == c2 47 | 48 | def forward(self, x): 49 | return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) 50 | 51 | 52 | class BottleneckCSP(nn.Module): 53 | # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks 54 | def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion 55 | super(BottleneckCSP, self).__init__() 56 | c_ = int(c2 * e) # hidden channels 57 | self.cv1 = Conv(c1, c_, 1, 1) 58 | self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False) 59 | self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False) 60 | self.cv4 = Conv(2 * c_, c2, 1, 1) 61 | self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3) 62 | self.act = nn.LeakyReLU(0.1, inplace=True) 63 | self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) 64 | 65 | def forward(self, x): 66 | y1 = self.cv3(self.m(self.cv1(x))) 67 | y2 = self.cv2(x) 68 | return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1)))) 69 | 70 | 71 | class SPP(nn.Module): 72 | # Spatial pyramid pooling layer used in YOLOv3-SPP 73 | def __init__(self, c1, c2, k=(5, 9, 13)): 74 | super(SPP, self).__init__() 75 | c_ = c1 // 2 # hidden channels 76 | self.cv1 = Conv(c1, c_, 1, 1) 77 | self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1) 78 | self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k]) 79 | 80 | def forward(self, x): 81 | x = self.cv1(x) 82 | return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1)) 83 | 84 | 85 | class Focus(nn.Module): 86 | # Focus wh information into c-space 87 | def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups 88 | super(Focus, self).__init__() 89 | self.conv = Conv(c1 * 4, c2, k, s, p, g, act) 90 | 91 | def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2) 92 | return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)) 93 | 94 | 95 | class Concat(nn.Module): 96 | # Concatenate a list of tensors along dimension 97 | def __init__(self, dimension=1): 98 | super(Concat, self).__init__() 99 | self.d = dimension 100 | 101 | def forward(self, x): 102 | return torch.cat(x, self.d) 103 | 104 | 105 | class NMS(nn.Module): 106 | # Non-Maximum Suppression (NMS) module 107 | conf = 0.25 # confidence threshold 108 | iou = 0.45 # IoU threshold 109 | classes = None # (optional list) filter by class 110 | 111 | def __init__(self): 112 | super(NMS, self).__init__() 113 | 114 | def forward(self, x): 115 | return non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) 116 | 117 | 118 | class autoShape(nn.Module): 119 | # input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS 120 | img_size = 640 # inference size (pixels) 121 | conf = 0.25 # NMS confidence threshold 122 | iou = 0.45 # NMS IoU threshold 123 | classes = None # (optional list) filter by class 124 | 125 | def __init__(self, model): 126 | super(autoShape, self).__init__() 127 | self.model = model 128 | 129 | def forward(self, x, size=640, augment=False, profile=False): 130 | # supports inference from various sources. For height=720, width=1280, RGB images example inputs are: 131 | # opencv: x = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(720,1280,3) 132 | # PIL: x = Image.open('image.jpg') # HWC x(720,1280,3) 133 | # numpy: x = np.zeros((720,1280,3)) # HWC 134 | # torch: x = torch.zeros(16,3,720,1280) # BCHW 135 | # multiple: x = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images 136 | 137 | p = next(self.model.parameters()) # for device and type 138 | if isinstance(x, torch.Tensor): # torch 139 | return self.model(x.to(p.device).type_as(p), augment, profile) # inference 140 | 141 | # Pre-process 142 | if not isinstance(x, list): 143 | x = [x] 144 | shape0, shape1 = [], [] # image and inference shapes 145 | batch = range(len(x)) # batch size 146 | for i in batch: 147 | x[i] = np.array(x[i])[:, :, :3] # up to 3 channels if png 148 | s = x[i].shape[:2] # HWC 149 | shape0.append(s) # image shape 150 | g = (size / max(s)) # gain 151 | shape1.append([y * g for y in s]) 152 | shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)] # inference shape 153 | x = [letterbox(x[i], new_shape=shape1, auto=False)[0] for i in batch] # pad 154 | x = np.stack(x, 0) if batch[-1] else x[0][None] # stack 155 | x = np.ascontiguousarray(x.transpose((0, 3, 1, 2))) # BHWC to BCHW 156 | x = torch.from_numpy(x).to(p.device).type_as(p) / 255. # uint8 to fp16/32 157 | 158 | # Inference 159 | x = self.model(x, augment, profile) # forward 160 | x = non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) # NMS 161 | 162 | # Post-process 163 | for i in batch: 164 | if x[i] is not None: 165 | x[i][:, :4] = scale_coords(shape1, x[i][:, :4], shape0[i]) 166 | return x 167 | 168 | 169 | class Flatten(nn.Module): 170 | # Use after nn.AdaptiveAvgPool2d(1) to remove last 2 dimensions 171 | @staticmethod 172 | def forward(x): 173 | return x.view(x.size(0), -1) 174 | 175 | 176 | class Classify(nn.Module): 177 | # Classification head, i.e. x(b,c1,20,20) to x(b,c2) 178 | def __init__(self, c1, c2, k=1, s=1, p=None, g=1): # ch_in, ch_out, kernel, stride, padding, groups 179 | super(Classify, self).__init__() 180 | self.aap = nn.AdaptiveAvgPool2d(1) # to x(b,c1,1,1) 181 | self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) # to x(b,c2,1,1) 182 | self.flat = Flatten() 183 | 184 | def forward(self, x): 185 | z = torch.cat([self.aap(y) for y in (x if isinstance(x, list) else [x])], 1) # cat if list 186 | return self.flat(self.conv(z)) # flatten to x(b,c2) 187 | -------------------------------------------------------------------------------- /YOLOv5/models/experimental.py: -------------------------------------------------------------------------------- 1 | # This file contains experimental modules 2 | 3 | import numpy as np 4 | import torch 5 | import torch.nn as nn 6 | 7 | from models.common import Conv, DWConv 8 | from utils.google_utils import attempt_download 9 | 10 | 11 | class CrossConv(nn.Module): 12 | # Cross Convolution Downsample 13 | def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False): 14 | # ch_in, ch_out, kernel, stride, groups, expansion, shortcut 15 | super(CrossConv, self).__init__() 16 | c_ = int(c2 * e) # hidden channels 17 | self.cv1 = Conv(c1, c_, (1, k), (1, s)) 18 | self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g) 19 | self.add = shortcut and c1 == c2 20 | 21 | def forward(self, x): 22 | return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) 23 | 24 | 25 | class C3(nn.Module): 26 | # Cross Convolution CSP 27 | def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion 28 | super(C3, self).__init__() 29 | c_ = int(c2 * e) # hidden channels 30 | self.cv1 = Conv(c1, c_, 1, 1) 31 | self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False) 32 | self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False) 33 | self.cv4 = Conv(2 * c_, c2, 1, 1) 34 | self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3) 35 | self.act = nn.LeakyReLU(0.1, inplace=True) 36 | self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)]) 37 | 38 | def forward(self, x): 39 | y1 = self.cv3(self.m(self.cv1(x))) 40 | y2 = self.cv2(x) 41 | return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1)))) 42 | 43 | 44 | class Sum(nn.Module): 45 | # Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070 46 | def __init__(self, n, weight=False): # n: number of inputs 47 | super(Sum, self).__init__() 48 | self.weight = weight # apply weights boolean 49 | self.iter = range(n - 1) # iter object 50 | if weight: 51 | self.w = nn.Parameter(-torch.arange(1., n) / 2, requires_grad=True) # layer weights 52 | 53 | def forward(self, x): 54 | y = x[0] # no weight 55 | if self.weight: 56 | w = torch.sigmoid(self.w) * 2 57 | for i in self.iter: 58 | y = y + x[i + 1] * w[i] 59 | else: 60 | for i in self.iter: 61 | y = y + x[i + 1] 62 | return y 63 | 64 | 65 | class GhostConv(nn.Module): 66 | # Ghost Convolution https://github.com/huawei-noah/ghostnet 67 | def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups 68 | super(GhostConv, self).__init__() 69 | c_ = c2 // 2 # hidden channels 70 | self.cv1 = Conv(c1, c_, k, s, None, g, act) 71 | self.cv2 = Conv(c_, c_, 5, 1, None, c_, act) 72 | 73 | def forward(self, x): 74 | y = self.cv1(x) 75 | return torch.cat([y, self.cv2(y)], 1) 76 | 77 | 78 | class GhostBottleneck(nn.Module): 79 | # Ghost Bottleneck https://github.com/huawei-noah/ghostnet 80 | def __init__(self, c1, c2, k, s): 81 | super(GhostBottleneck, self).__init__() 82 | c_ = c2 // 2 83 | self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # pw 84 | DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw 85 | GhostConv(c_, c2, 1, 1, act=False)) # pw-linear 86 | self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False), 87 | Conv(c1, c2, 1, 1, act=False)) if s == 2 else nn.Identity() 88 | 89 | def forward(self, x): 90 | return self.conv(x) + self.shortcut(x) 91 | 92 | 93 | class MixConv2d(nn.Module): 94 | # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595 95 | def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): 96 | super(MixConv2d, self).__init__() 97 | groups = len(k) 98 | if equal_ch: # equal c_ per group 99 | i = torch.linspace(0, groups - 1E-6, c2).floor() # c2 indices 100 | c_ = [(i == g).sum() for g in range(groups)] # intermediate channels 101 | else: # equal weight.numel() per group 102 | b = [c2] + [0] * groups 103 | a = np.eye(groups + 1, groups, k=-1) 104 | a -= np.roll(a, 1, axis=1) 105 | a *= np.array(k) ** 2 106 | a[0] = 1 107 | c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b 108 | 109 | self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)]) 110 | self.bn = nn.BatchNorm2d(c2) 111 | self.act = nn.LeakyReLU(0.1, inplace=True) 112 | 113 | def forward(self, x): 114 | return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) 115 | 116 | 117 | class Ensemble(nn.ModuleList): 118 | # Ensemble of models 119 | def __init__(self): 120 | super(Ensemble, self).__init__() 121 | 122 | def forward(self, x, augment=False): 123 | y = [] 124 | for module in self: 125 | y.append(module(x, augment)[0]) 126 | # y = torch.stack(y).max(0)[0] # max ensemble 127 | # y = torch.cat(y, 1) # nms ensemble 128 | y = torch.stack(y).mean(0) # mean ensemble 129 | return y, None # inference, train output 130 | 131 | 132 | def attempt_load(weights, map_location=None): 133 | # Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a 134 | model = Ensemble() 135 | for w in weights if isinstance(weights, list) else [weights]: 136 | attempt_download(w) 137 | model.append(torch.load(w, map_location=map_location)['model'].float().fuse().eval()) # load FP32 model 138 | 139 | if len(model) == 1: 140 | return model[-1] # return model 141 | else: 142 | print('Ensemble created with %s\n' % weights) 143 | for k in ['names', 'stride']: 144 | setattr(model, k, getattr(model[-1], k)) 145 | return model # return ensemble 146 | -------------------------------------------------------------------------------- /YOLOv5/models/export.py: -------------------------------------------------------------------------------- 1 | """Exports a YOLOv5 *.pt model to ONNX and TorchScript formats 2 | 3 | Usage: 4 | $ export PYTHONPATH="$PWD" && python models/export.py --weights ./weights/yolov5s.pt --img 640 --batch 1 5 | """ 6 | 7 | import argparse 8 | import sys 9 | import time 10 | import os 11 | 12 | #sys.path.append('./../../yolo-v5') # to run '$ python *.py' files in subdirectories 13 | sys.path.insert(1, './../yolo-v5') # correct path 14 | 15 | import torch 16 | import torch.nn as nn 17 | 18 | import models 19 | from models.experimental import attempt_load 20 | from utils.activations import Hardswish 21 | from utils.general import set_logging, check_img_size 22 | 23 | if __name__ == '__main__': 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument('--weights', type=str, default='./yolov5s.pt', help='weights path') # from yolov5/models/ 26 | parser.add_argument('--img-size', nargs='+', type=int, default=[1280, 736], help='image size') # height, width default=[640, 640] 27 | parser.add_argument('--batch-size', type=int, default=1, help='batch size') 28 | opt = parser.parse_args() 29 | opt.img_size *= 2 if len(opt.img_size) == 1 else 1 # expand 30 | print(opt) 31 | set_logging() 32 | t = time.time() 33 | 34 | # Load PyTorch model 35 | model = attempt_load(opt.weights, map_location=torch.device('cpu')) # load FP32 model 36 | labels = model.names 37 | 38 | # Checks 39 | gs = int(max(model.stride)) # grid size (max stride) 40 | opt.img_size = [check_img_size(x, gs) for x in opt.img_size] # verify img_size are gs-multiples 41 | 42 | # Input 43 | img = torch.zeros(opt.batch_size, 3, *opt.img_size) # image size(1,3,320,192) iDetection 44 | 45 | # Update model 46 | for k, m in model.named_modules(): 47 | m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility 48 | if isinstance(m, models.common.Conv) and isinstance(m.act, nn.Hardswish): 49 | m.act = Hardswish() # assign activation 50 | # if isinstance(m, models.yolo.Detect): 51 | # m.forward = m.forward_export # assign forward (optional) 52 | #model.model[-1].export = True # set Detect() layer export=True 53 | model.model[-1].export = False # Correction 54 | y = model(img) # dry run 55 | 56 | # TorchScript export 57 | try: 58 | print('\nStarting TorchScript export with torch %s...' % torch.__version__) 59 | f = opt.weights.replace('.pt', '.torchscript.pt') # filename 60 | ts = torch.jit.trace(model, img) 61 | ts.save(f) 62 | print('TorchScript export success, saved as %s' % f) 63 | except Exception as e: 64 | print('TorchScript export failure: %s' % e) 65 | 66 | # ONNX export 67 | try: 68 | import onnx 69 | 70 | print('\nStarting ONNX export with onnx %s...' % onnx.__version__) 71 | f = opt.weights.replace('.pt', '.onnx') # filename 72 | torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'], 73 | output_names=['classes', 'boxes'] if y is None else ['output']) 74 | 75 | # Checks 76 | onnx_model = onnx.load(f) # load onnx model 77 | onnx.checker.check_model(onnx_model) # check onnx model 78 | # print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable model 79 | print('ONNX export success, saved as %s' % f) 80 | except Exception as e: 81 | print('ONNX export failure: %s' % e) 82 | 83 | # CoreML export 84 | try: 85 | import coremltools as ct 86 | 87 | print('\nStarting CoreML export with coremltools %s...' % ct.__version__) 88 | # convert model from torchscript and apply pixel scaling as per detect.py 89 | model = ct.convert(ts, inputs=[ct.ImageType(name='image', shape=img.shape, scale=1 / 255.0, bias=[0, 0, 0])]) 90 | f = opt.weights.replace('.pt', '.mlmodel') # filename 91 | model.save(f) 92 | print('CoreML export success, saved as %s' % f) 93 | except Exception as e: 94 | print('CoreML export failure: %s' % e) 95 | 96 | # Finish 97 | print('\nExport complete (%.2fs). Visualize with https://github.com/lutzroeder/netron.' % (time.time() - t)) 98 | -------------------------------------------------------------------------------- /YOLOv5/models/hub/yolov3-spp.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.0 # model depth multiple 4 | width_multiple: 1.0 # layer channel multiple 5 | 6 | # anchors 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 | -------------------------------------------------------------------------------- /YOLOv5/models/hub/yolov5-fpn.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.0 # model depth multiple 4 | width_multiple: 1.0 # layer channel multiple 5 | 6 | # anchors 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 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, Bottleneck, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 6, BottleneckCSP, [1024]], # 9 25 | ] 26 | 27 | # YOLOv5 FPN head 28 | head: 29 | [[-1, 3, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [256, False]], # 18 (P3/8-small) 40 | 41 | [[18, 14, 10], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 42 | ] 43 | -------------------------------------------------------------------------------- /YOLOv5/models/hub/yolov5-panet.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.0 # model depth multiple 4 | width_multiple: 1.0 # layer channel multiple 5 | 6 | # anchors 7 | anchors: 8 | - [116,90, 156,198, 373,326] # P5/32 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [10,13, 16,30, 33,23] # P3/8 11 | 12 | # YOLOv5 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P5, P4, P3) 48 | ] 49 | -------------------------------------------------------------------------------- /YOLOv5/models/yolo.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import logging 3 | import sys 4 | from copy import deepcopy 5 | from pathlib import Path 6 | 7 | import math 8 | 9 | sys.path.append('./') # to run '$ python *.py' files in subdirectories 10 | logger = logging.getLogger(__name__) 11 | 12 | import torch 13 | import torch.nn as nn 14 | 15 | from models.common import Conv, Bottleneck, SPP, DWConv, Focus, BottleneckCSP, Concat, NMS, autoShape 16 | from models.experimental import MixConv2d, CrossConv, C3 17 | from utils.general import check_anchor_order, make_divisible, check_file, set_logging 18 | from utils.torch_utils import time_synchronized, fuse_conv_and_bn, model_info, scale_img, initialize_weights, \ 19 | select_device, copy_attr 20 | 21 | 22 | class Detect(nn.Module): 23 | stride = None # strides computed during build 24 | export = False # onnx export 25 | 26 | def __init__(self, nc=80, anchors=(), ch=()): # detection layer 27 | super(Detect, self).__init__() 28 | self.nc = nc # number of classes 29 | self.no = nc + 5 # number of outputs per anchor 30 | self.nl = len(anchors) # number of detection layers 31 | self.na = len(anchors[0]) // 2 # number of anchors 32 | self.grid = [torch.zeros(1)] * self.nl # init grid 33 | a = torch.tensor(anchors).float().view(self.nl, -1, 2) 34 | self.register_buffer('anchors', a) # shape(nl,na,2) 35 | self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2) 36 | self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv 37 | 38 | def forward(self, x): 39 | # x = x.copy() # for profiling 40 | z = [] # inference output 41 | self.training |= self.export 42 | for i in range(self.nl): 43 | x[i] = self.m[i](x[i]) # conv 44 | bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) 45 | x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() 46 | 47 | if not self.training: # inference 48 | if self.grid[i].shape[2:4] != x[i].shape[2:4]: 49 | self.grid[i] = self._make_grid(nx, ny).to(x[i].device) 50 | 51 | y = x[i].sigmoid() 52 | y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy 53 | y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh 54 | z.append(y.view(bs, -1, self.no)) 55 | 56 | return x if self.training else (torch.cat(z, 1), x) 57 | 58 | @staticmethod 59 | def _make_grid(nx=20, ny=20): 60 | yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)]) 61 | return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float() 62 | 63 | 64 | class Model(nn.Module): 65 | def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None): # model, input channels, number of classes 66 | super(Model, self).__init__() 67 | if isinstance(cfg, dict): 68 | self.yaml = cfg # model dict 69 | else: # is *.yaml 70 | import yaml # for torch hub 71 | self.yaml_file = Path(cfg).name 72 | with open(cfg) as f: 73 | self.yaml = yaml.load(f, Loader=yaml.FullLoader) # model dict 74 | 75 | # Define model 76 | if nc and nc != self.yaml['nc']: 77 | print('Overriding model.yaml nc=%g with nc=%g' % (self.yaml['nc'], nc)) 78 | self.yaml['nc'] = nc # override yaml value 79 | self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist, ch_out 80 | # print([x.shape for x in self.forward(torch.zeros(1, ch, 64, 64))]) 81 | 82 | # Build strides, anchors 83 | m = self.model[-1] # Detect() 84 | if isinstance(m, Detect): 85 | s = 128 # 2x min stride 86 | m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward 87 | m.anchors /= m.stride.view(-1, 1, 1) 88 | check_anchor_order(m) 89 | self.stride = m.stride 90 | self._initialize_biases() # only run once 91 | # print('Strides: %s' % m.stride.tolist()) 92 | 93 | # Init weights, biases 94 | initialize_weights(self) 95 | self.info() 96 | print('') 97 | 98 | def forward(self, x, augment=False, profile=False): 99 | if augment: 100 | img_size = x.shape[-2:] # height, width 101 | s = [1, 0.83, 0.67] # scales 102 | f = [None, 3, None] # flips (2-ud, 3-lr) 103 | y = [] # outputs 104 | for si, fi in zip(s, f): 105 | xi = scale_img(x.flip(fi) if fi else x, si) 106 | yi = self.forward_once(xi)[0] # forward 107 | # cv2.imwrite('img%g.jpg' % s, 255 * xi[0].numpy().transpose((1, 2, 0))[:, :, ::-1]) # save 108 | yi[..., :4] /= si # de-scale 109 | if fi == 2: 110 | yi[..., 1] = img_size[0] - yi[..., 1] # de-flip ud 111 | elif fi == 3: 112 | yi[..., 0] = img_size[1] - yi[..., 0] # de-flip lr 113 | y.append(yi) 114 | return torch.cat(y, 1), None # augmented inference, train 115 | else: 116 | return self.forward_once(x, profile) # single-scale inference, train 117 | 118 | def forward_once(self, x, profile=False): 119 | y, dt = [], [] # outputs 120 | for m in self.model: 121 | if m.f != -1: # if not from previous layer 122 | x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers 123 | 124 | if profile: 125 | try: 126 | import thop 127 | o = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 # FLOPS 128 | except: 129 | o = 0 130 | t = time_synchronized() 131 | for _ in range(10): 132 | _ = m(x) 133 | dt.append((time_synchronized() - t) * 100) 134 | print('%10.1f%10.0f%10.1fms %-40s' % (o, m.np, dt[-1], m.type)) 135 | 136 | x = m(x) # run 137 | y.append(x if m.i in self.save else None) # save output 138 | 139 | if profile: 140 | print('%.1fms total' % sum(dt)) 141 | return x 142 | 143 | def _initialize_biases(self, cf=None): # initialize biases into Detect(), cf is class frequency 144 | # https://arxiv.org/abs/1708.02002 section 3.3 145 | # cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1. 146 | m = self.model[-1] # Detect() module 147 | for mi, s in zip(m.m, m.stride): # from 148 | b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85) 149 | b[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image) 150 | b[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls 151 | mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) 152 | 153 | def _print_biases(self): 154 | m = self.model[-1] # Detect() module 155 | for mi in m.m: # from 156 | b = mi.bias.detach().view(m.na, -1).T # conv.bias(255) to (3,85) 157 | print(('%6g Conv2d.bias:' + '%10.3g' * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean())) 158 | 159 | # def _print_weights(self): 160 | # for m in self.model.modules(): 161 | # if type(m) is Bottleneck: 162 | # print('%10.3g' % (m.w.detach().sigmoid() * 2)) # shortcut weights 163 | 164 | def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers 165 | print('Fusing layers... ') 166 | for m in self.model.modules(): 167 | if type(m) is Conv and hasattr(m, 'bn'): 168 | m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatability 169 | m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv 170 | delattr(m, 'bn') # remove batchnorm 171 | m.forward = m.fuseforward # update forward 172 | self.info() 173 | return self 174 | 175 | def nms(self, mode=True): # add or remove NMS module 176 | present = type(self.model[-1]) is NMS # last layer is NMS 177 | if mode and not present: 178 | print('Adding NMS... ') 179 | m = NMS() # module 180 | m.f = -1 # from 181 | m.i = self.model[-1].i + 1 # index 182 | self.model.add_module(name='%s' % m.i, module=m) # add 183 | self.eval() 184 | elif not mode and present: 185 | print('Removing NMS... ') 186 | self.model = self.model[:-1] # remove 187 | return self 188 | 189 | def autoshape(self): # add autoShape module 190 | print('Adding autoShape... ') 191 | m = autoShape(self) # wrap model 192 | copy_attr(m, self, include=('yaml', 'nc', 'hyp', 'names', 'stride'), exclude=()) # copy attributes 193 | return m 194 | 195 | def info(self, verbose=False): # print model information 196 | model_info(self, verbose) 197 | 198 | 199 | def parse_model(d, ch): # model_dict, input_channels(3) 200 | logger.info('\n%3s%18s%3s%10s %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments')) 201 | anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple'] 202 | na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors 203 | no = na * (nc + 5) # number of outputs = anchors * (classes + 5) 204 | 205 | layers, save, c2 = [], [], ch[-1] # layers, savelist, ch out 206 | for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']): # from, number, module, args 207 | m = eval(m) if isinstance(m, str) else m # eval strings 208 | for j, a in enumerate(args): 209 | try: 210 | args[j] = eval(a) if isinstance(a, str) else a # eval strings 211 | except: 212 | pass 213 | 214 | n = max(round(n * gd), 1) if n > 1 else n # depth gain 215 | if m in [Conv, Bottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP, C3]: 216 | c1, c2 = ch[f], args[0] 217 | 218 | # Normal 219 | # if i > 0 and args[0] != no: # channel expansion factor 220 | # ex = 1.75 # exponential (default 2.0) 221 | # e = math.log(c2 / ch[1]) / math.log(2) 222 | # c2 = int(ch[1] * ex ** e) 223 | # if m != Focus: 224 | 225 | c2 = make_divisible(c2 * gw, 8) if c2 != no else c2 226 | 227 | # Experimental 228 | # if i > 0 and args[0] != no: # channel expansion factor 229 | # ex = 1 + gw # exponential (default 2.0) 230 | # ch1 = 32 # ch[1] 231 | # e = math.log(c2 / ch1) / math.log(2) # level 1-n 232 | # c2 = int(ch1 * ex ** e) 233 | # if m != Focus: 234 | # c2 = make_divisible(c2, 8) if c2 != no else c2 235 | 236 | args = [c1, c2, *args[1:]] 237 | if m in [BottleneckCSP, C3]: 238 | args.insert(2, n) 239 | n = 1 240 | elif m is nn.BatchNorm2d: 241 | args = [ch[f]] 242 | elif m is Concat: 243 | c2 = sum([ch[-1 if x == -1 else x + 1] for x in f]) 244 | elif m is Detect: 245 | args.append([ch[x + 1] for x in f]) 246 | if isinstance(args[1], int): # number of anchors 247 | args[1] = [list(range(args[1] * 2))] * len(f) 248 | else: 249 | c2 = ch[f] 250 | 251 | m_ = nn.Sequential(*[m(*args) for _ in range(n)]) if n > 1 else m(*args) # module 252 | t = str(m)[8:-2].replace('__main__.', '') # module type 253 | np = sum([x.numel() for x in m_.parameters()]) # number params 254 | m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params 255 | logger.info('%3s%18s%3s%10.0f %-40s%-30s' % (i, f, n, np, t, args)) # print 256 | save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist 257 | layers.append(m_) 258 | ch.append(c2) 259 | return nn.Sequential(*layers), sorted(save) 260 | 261 | 262 | if __name__ == '__main__': 263 | parser = argparse.ArgumentParser() 264 | parser.add_argument('--cfg', type=str, default='yolov5s.yaml', help='model.yaml') 265 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 266 | opt = parser.parse_args() 267 | opt.cfg = check_file(opt.cfg) # check file 268 | set_logging() 269 | device = select_device(opt.device) 270 | 271 | # Create model 272 | model = Model(opt.cfg).to(device) 273 | model.train() 274 | 275 | # Profile 276 | # img = torch.rand(8 if torch.cuda.is_available() else 1, 3, 640, 640).to(device) 277 | # y = model(img, profile=True) 278 | 279 | # Tensorboard 280 | # from torch.utils.tensorboard import SummaryWriter 281 | # tb_writer = SummaryWriter() 282 | # print("Run 'tensorboard --logdir=models/runs' to view tensorboard at http://localhost:6006/") 283 | # tb_writer.add_graph(model.model, img) # add model to tensorboard 284 | # tb_writer.add_image('test', img[0], dataformats='CWH') # add model to tensorboard 285 | -------------------------------------------------------------------------------- /YOLOv5/models/yolov5l.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.0 # model depth multiple 4 | width_multiple: 1.0 # layer channel multiple 5 | 6 | # anchors 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 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /YOLOv5/models/yolov5m.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 0.67 # model depth multiple 4 | width_multiple: 0.75 # layer channel multiple 5 | 6 | # anchors 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 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /YOLOv5/models/yolov5s.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 3 # number of classes 3 | depth_multiple: 0.33 # model depth multiple 4 | width_multiple: 0.50 # layer channel multiple 5 | 6 | # anchors 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 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /YOLOv5/models/yolov5x.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.33 # model depth multiple 4 | width_multiple: 1.25 # layer channel multiple 5 | 6 | # anchors 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 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [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, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /YOLOv5/sotabench.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import glob 3 | import os 4 | import shutil 5 | from pathlib import Path 6 | 7 | import numpy as np 8 | import torch 9 | import yaml 10 | from sotabencheval.object_detection import COCOEvaluator 11 | from sotabencheval.utils import is_server 12 | from tqdm import tqdm 13 | 14 | from models.experimental import attempt_load 15 | from utils.datasets import create_dataloader 16 | from utils.general import ( 17 | coco80_to_coco91_class, check_dataset, check_file, check_img_size, compute_loss, non_max_suppression, scale_coords, 18 | xyxy2xywh, clip_coords, set_logging) 19 | from utils.torch_utils import select_device, time_synchronized 20 | 21 | DATA_ROOT = './.data/vision/coco' if is_server() else '../coco' # sotabench data dir 22 | 23 | 24 | def test(data, 25 | weights=None, 26 | batch_size=16, 27 | imgsz=640, 28 | conf_thres=0.001, 29 | iou_thres=0.6, # for NMS 30 | save_json=False, 31 | single_cls=False, 32 | augment=False, 33 | verbose=False, 34 | model=None, 35 | dataloader=None, 36 | save_dir='', 37 | merge=False, 38 | save_txt=False): 39 | # Initialize/load model and set device 40 | training = model is not None 41 | if training: # called by train.py 42 | device = next(model.parameters()).device # get model device 43 | 44 | else: # called directly 45 | set_logging() 46 | device = select_device(opt.device, batch_size=batch_size) 47 | merge, save_txt = opt.merge, opt.save_txt # use Merge NMS, save *.txt labels 48 | if save_txt: 49 | out = Path('inference/output') 50 | if os.path.exists(out): 51 | shutil.rmtree(out) # delete output folder 52 | os.makedirs(out) # make new output folder 53 | 54 | # Remove previous 55 | for f in glob.glob(str(Path(save_dir) / 'test_batch*.jpg')): 56 | os.remove(f) 57 | 58 | # Load model 59 | model = attempt_load(weights, map_location=device) # load FP32 model 60 | imgsz = check_img_size(imgsz, s=model.stride.max()) # check img_size 61 | 62 | # Multi-GPU disabled, incompatible with .half() https://github.com/ultralytics/yolov5/issues/99 63 | # if device.type != 'cpu' and torch.cuda.device_count() > 1: 64 | # model = nn.DataParallel(model) 65 | 66 | # Half 67 | half = device.type != 'cpu' # half precision only supported on CUDA 68 | if half: 69 | model.half() 70 | 71 | # Configure 72 | model.eval() 73 | with open(data) as f: 74 | data = yaml.load(f, Loader=yaml.FullLoader) # model dict 75 | check_dataset(data) # check 76 | nc = 1 if single_cls else int(data['nc']) # number of classes 77 | iouv = torch.linspace(0.5, 0.95, 10).to(device) # iou vector for mAP@0.5:0.95 78 | niou = iouv.numel() 79 | 80 | # Dataloader 81 | if not training: 82 | img = torch.zeros((1, 3, imgsz, imgsz), device=device) # init img 83 | _ = model(img.half() if half else img) if device.type != 'cpu' else None # run once 84 | path = data['test'] if opt.task == 'test' else data['val'] # path to val/test images 85 | dataloader = create_dataloader(path, imgsz, batch_size, model.stride.max(), opt, 86 | hyp=None, augment=False, cache=True, pad=0.5, rect=True)[0] 87 | 88 | seen = 0 89 | names = model.names if hasattr(model, 'names') else model.module.names 90 | coco91class = coco80_to_coco91_class() 91 | s = ('%20s' + '%12s' * 6) % ('Class', 'Images', 'Targets', 'P', 'R', 'mAP@.5', 'mAP@.5:.95') 92 | p, r, f1, mp, mr, map50, map, t0, t1 = 0., 0., 0., 0., 0., 0., 0., 0., 0. 93 | loss = torch.zeros(3, device=device) 94 | jdict, stats, ap, ap_class = [], [], [], [] 95 | evaluator = COCOEvaluator(root=DATA_ROOT, model_name=opt.weights.replace('.pt', '')) 96 | for batch_i, (img, targets, paths, shapes) in enumerate(tqdm(dataloader, desc=s)): 97 | img = img.to(device, non_blocking=True) 98 | img = img.half() if half else img.float() # uint8 to fp16/32 99 | img /= 255.0 # 0 - 255 to 0.0 - 1.0 100 | targets = targets.to(device) 101 | nb, _, height, width = img.shape # batch size, channels, height, width 102 | whwh = torch.Tensor([width, height, width, height]).to(device) 103 | 104 | # Disable gradients 105 | with torch.no_grad(): 106 | # Run model 107 | t = time_synchronized() 108 | inf_out, train_out = model(img, augment=augment) # inference and training outputs 109 | t0 += time_synchronized() - t 110 | 111 | # Compute loss 112 | if training: # if model has loss hyperparameters 113 | loss += compute_loss([x.float() for x in train_out], targets, model)[1][:3] # box, obj, cls 114 | 115 | # Run NMS 116 | t = time_synchronized() 117 | output = non_max_suppression(inf_out, conf_thres=conf_thres, iou_thres=iou_thres, merge=merge) 118 | t1 += time_synchronized() - t 119 | 120 | # Statistics per image 121 | for si, pred in enumerate(output): 122 | labels = targets[targets[:, 0] == si, 1:] 123 | nl = len(labels) 124 | tcls = labels[:, 0].tolist() if nl else [] # target class 125 | seen += 1 126 | 127 | if pred is None: 128 | if nl: 129 | stats.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls)) 130 | continue 131 | 132 | # Append to text file 133 | if save_txt: 134 | gn = torch.tensor(shapes[si][0])[[1, 0, 1, 0]] # normalization gain whwh 135 | x = pred.clone() 136 | x[:, :4] = scale_coords(img[si].shape[1:], x[:, :4], shapes[si][0], shapes[si][1]) # to original 137 | for *xyxy, conf, cls in x: 138 | xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh 139 | with open(str(out / Path(paths[si]).stem) + '.txt', 'a') as f: 140 | f.write(('%g ' * 5 + '\n') % (cls, *xywh)) # label format 141 | 142 | # Clip boxes to image bounds 143 | clip_coords(pred, (height, width)) 144 | 145 | # Append to pycocotools JSON dictionary 146 | if save_json: 147 | # [{"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}, ... 148 | image_id = Path(paths[si]).stem 149 | box = pred[:, :4].clone() # xyxy 150 | scale_coords(img[si].shape[1:], box, shapes[si][0], shapes[si][1]) # to original shape 151 | box = xyxy2xywh(box) # xywh 152 | box[:, :2] -= box[:, 2:] / 2 # xy center to top-left corner 153 | for p, b in zip(pred.tolist(), box.tolist()): 154 | result = {'image_id': int(image_id) if image_id.isnumeric() else image_id, 155 | 'category_id': coco91class[int(p[5])], 156 | 'bbox': [round(x, 3) for x in b], 157 | 'score': round(p[4], 5)} 158 | jdict.append(result) 159 | 160 | #evaluator.add([result]) 161 | #if evaluator.cache_exists: 162 | # break 163 | 164 | # # Assign all predictions as incorrect 165 | # correct = torch.zeros(pred.shape[0], niou, dtype=torch.bool, device=device) 166 | # if nl: 167 | # detected = [] # target indices 168 | # tcls_tensor = labels[:, 0] 169 | # 170 | # # target boxes 171 | # tbox = xywh2xyxy(labels[:, 1:5]) * whwh 172 | # 173 | # # Per target class 174 | # for cls in torch.unique(tcls_tensor): 175 | # ti = (cls == tcls_tensor).nonzero(as_tuple=False).view(-1) # prediction indices 176 | # pi = (cls == pred[:, 5]).nonzero(as_tuple=False).view(-1) # target indices 177 | # 178 | # # Search for detections 179 | # if pi.shape[0]: 180 | # # Prediction to target ious 181 | # ious, i = box_iou(pred[pi, :4], tbox[ti]).max(1) # best ious, indices 182 | # 183 | # # Append detections 184 | # detected_set = set() 185 | # for j in (ious > iouv[0]).nonzero(as_tuple=False): 186 | # d = ti[i[j]] # detected target 187 | # if d.item() not in detected_set: 188 | # detected_set.add(d.item()) 189 | # detected.append(d) 190 | # correct[pi[j]] = ious[j] > iouv # iou_thres is 1xn 191 | # if len(detected) == nl: # all targets already located in image 192 | # break 193 | # 194 | # # Append statistics (correct, conf, pcls, tcls) 195 | # stats.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls)) 196 | 197 | # # Plot images 198 | # if batch_i < 1: 199 | # f = Path(save_dir) / ('test_batch%g_gt.jpg' % batch_i) # filename 200 | # plot_images(img, targets, paths, str(f), names) # ground truth 201 | # f = Path(save_dir) / ('test_batch%g_pred.jpg' % batch_i) 202 | # plot_images(img, output_to_target(output, width, height), paths, str(f), names) # predictions 203 | 204 | evaluator.add(jdict) 205 | evaluator.save() 206 | 207 | # # Compute statistics 208 | # stats = [np.concatenate(x, 0) for x in zip(*stats)] # to numpy 209 | # if len(stats) and stats[0].any(): 210 | # p, r, ap, f1, ap_class = ap_per_class(*stats) 211 | # p, r, ap50, ap = p[:, 0], r[:, 0], ap[:, 0], ap.mean(1) # [P, R, AP@0.5, AP@0.5:0.95] 212 | # mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean() 213 | # nt = np.bincount(stats[3].astype(np.int64), minlength=nc) # number of targets per class 214 | # else: 215 | # nt = torch.zeros(1) 216 | # 217 | # # Print results 218 | # pf = '%20s' + '%12.3g' * 6 # print format 219 | # print(pf % ('all', seen, nt.sum(), mp, mr, map50, map)) 220 | # 221 | # # Print results per class 222 | # if verbose and nc > 1 and len(stats): 223 | # for i, c in enumerate(ap_class): 224 | # print(pf % (names[c], seen, nt[c], p[i], r[i], ap50[i], ap[i])) 225 | # 226 | # # Print speeds 227 | # t = tuple(x / seen * 1E3 for x in (t0, t1, t0 + t1)) + (imgsz, imgsz, batch_size) # tuple 228 | # if not training: 229 | # print('Speed: %.1f/%.1f/%.1f ms inference/NMS/total per %gx%g image at batch-size %g' % t) 230 | # 231 | # # Save JSON 232 | # if save_json and len(jdict): 233 | # f = 'detections_val2017_%s_results.json' % \ 234 | # (weights.split(os.sep)[-1].replace('.pt', '') if isinstance(weights, str) else '') # filename 235 | # print('\nCOCO mAP with pycocotools... saving %s...' % f) 236 | # with open(f, 'w') as file: 237 | # json.dump(jdict, file) 238 | # 239 | # try: # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb 240 | # from pycocotools.coco import COCO 241 | # from pycocotools.cocoeval import COCOeval 242 | # 243 | # imgIds = [int(Path(x).stem) for x in dataloader.dataset.img_files] 244 | # cocoGt = COCO(glob.glob('../coco/annotations/instances_val*.json')[0]) # initialize COCO ground truth api 245 | # cocoDt = cocoGt.loadRes(f) # initialize COCO pred api 246 | # cocoEval = COCOeval(cocoGt, cocoDt, 'bbox') 247 | # cocoEval.params.imgIds = imgIds # image IDs to evaluate 248 | # cocoEval.evaluate() 249 | # cocoEval.accumulate() 250 | # cocoEval.summarize() 251 | # map, map50 = cocoEval.stats[:2] # update results (mAP@0.5:0.95, mAP@0.5) 252 | # except Exception as e: 253 | # print('ERROR: pycocotools unable to run: %s' % e) 254 | # 255 | # # Return results 256 | # model.float() # for training 257 | # maps = np.zeros(nc) + map 258 | # for i, c in enumerate(ap_class): 259 | # maps[c] = ap[i] 260 | # return (mp, mr, map50, map, *(loss.cpu() / len(dataloader)).tolist()), maps, t 261 | 262 | 263 | if __name__ == '__main__': 264 | parser = argparse.ArgumentParser(prog='test.py') 265 | parser.add_argument('--weights', nargs='+', type=str, default='yolov5s.pt', help='model.pt path(s)') 266 | parser.add_argument('--data', type=str, default='data/coco.yaml', help='*.data path') 267 | parser.add_argument('--batch-size', type=int, default=32, help='size of each image batch') 268 | parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)') 269 | parser.add_argument('--conf-thres', type=float, default=0.001, help='object confidence threshold') 270 | parser.add_argument('--iou-thres', type=float, default=0.65, help='IOU threshold for NMS') 271 | parser.add_argument('--save-json', action='store_true', help='save a cocoapi-compatible JSON results file') 272 | parser.add_argument('--task', default='val', help="'val', 'test', 'study'") 273 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 274 | parser.add_argument('--single-cls', action='store_true', help='treat as single-class dataset') 275 | parser.add_argument('--augment', action='store_true', help='augmented inference') 276 | parser.add_argument('--merge', action='store_true', help='use Merge NMS') 277 | parser.add_argument('--verbose', action='store_true', help='report mAP by class') 278 | parser.add_argument('--save-txt', action='store_true', help='save results to *.txt') 279 | opt = parser.parse_args() 280 | opt.save_json |= opt.data.endswith('coco.yaml') 281 | opt.data = check_file(opt.data) # check file 282 | print(opt) 283 | 284 | if opt.task in ['val', 'test']: # run normally 285 | test(opt.data, 286 | opt.weights, 287 | opt.batch_size, 288 | opt.img_size, 289 | opt.conf_thres, 290 | opt.iou_thres, 291 | opt.save_json, 292 | opt.single_cls, 293 | opt.augment, 294 | opt.verbose) 295 | 296 | elif opt.task == 'study': # run over a range of settings and save/plot 297 | for weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt']: 298 | f = 'study_%s_%s.txt' % (Path(opt.data).stem, Path(weights).stem) # filename to save to 299 | x = list(range(320, 800, 64)) # x axis 300 | y = [] # y axis 301 | for i in x: # img-size 302 | print('\nRunning %s point %s...' % (f, i)) 303 | r, _, t = test(opt.data, weights, opt.batch_size, i, opt.conf_thres, opt.iou_thres, opt.save_json) 304 | y.append(r + t) # results and times 305 | np.savetxt(f, y, fmt='%10.4g') # save 306 | os.system('zip -r study.zip study_*.txt') 307 | # utils.general.plot_study_txt(f, x) # plot -------------------------------------------------------------------------------- /YOLOv5/test.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import glob 3 | import json 4 | import os 5 | import shutil 6 | from pathlib import Path 7 | 8 | import numpy as np 9 | import torch 10 | import yaml 11 | from tqdm import tqdm 12 | 13 | from models.experimental import attempt_load 14 | from utils.datasets import create_dataloader 15 | from utils.general import ( 16 | coco80_to_coco91_class, check_dataset, check_file, check_img_size, compute_loss, non_max_suppression, scale_coords, 17 | xyxy2xywh, clip_coords, plot_images, xywh2xyxy, box_iou, output_to_target, ap_per_class, set_logging) 18 | from utils.torch_utils import select_device, time_synchronized 19 | 20 | 21 | def test(data, 22 | weights=None, 23 | batch_size=16, 24 | imgsz=640, 25 | conf_thres=0.001, 26 | iou_thres=0.6, # for NMS 27 | save_json=False, 28 | single_cls=False, 29 | augment=False, 30 | verbose=False, 31 | model=None, 32 | dataloader=None, 33 | save_dir=Path(''), # for saving images 34 | save_txt=False, # for auto-labelling 35 | save_conf=False, 36 | plots=True): 37 | # Initialize/load model and set device 38 | training = model is not None 39 | if training: # called by train.py 40 | device = next(model.parameters()).device # get model device 41 | 42 | else: # called directly 43 | set_logging() 44 | device = select_device(opt.device, batch_size=batch_size) 45 | save_txt = opt.save_txt # save *.txt labels 46 | 47 | # Remove previous 48 | if os.path.exists(save_dir): 49 | shutil.rmtree(save_dir) # delete dir 50 | os.makedirs(save_dir) # make new dir 51 | 52 | if save_txt: 53 | out = save_dir / 'autolabels' 54 | if os.path.exists(out): 55 | shutil.rmtree(out) # delete dir 56 | os.makedirs(out) # make new dir 57 | 58 | # Load model 59 | model = attempt_load(weights, map_location=device) # load FP32 model 60 | imgsz = check_img_size(imgsz, s=model.stride.max()) # check img_size 61 | 62 | # Multi-GPU disabled, incompatible with .half() https://github.com/ultralytics/yolov5/issues/99 63 | # if device.type != 'cpu' and torch.cuda.device_count() > 1: 64 | # model = nn.DataParallel(model) 65 | 66 | # Half 67 | half = device.type != 'cpu' # half precision only supported on CUDA 68 | if half: 69 | model.half() 70 | 71 | # Configure 72 | model.eval() 73 | with open(data) as f: 74 | data = yaml.load(f, Loader=yaml.FullLoader) # model dict 75 | check_dataset(data) # check 76 | nc = 1 if single_cls else int(data['nc']) # number of classes 77 | iouv = torch.linspace(0.5, 0.95, 10).to(device) # iou vector for mAP@0.5:0.95 78 | niou = iouv.numel() 79 | 80 | # Dataloader 81 | if not training: 82 | img = torch.zeros((1, 3, imgsz, imgsz), device=device) # init img 83 | _ = model(img.half() if half else img) if device.type != 'cpu' else None # run once 84 | path = data['test'] if opt.task == 'test' else data['val'] # path to val/test images 85 | dataloader = create_dataloader(path, imgsz, batch_size, model.stride.max(), opt, 86 | hyp=None, augment=False, cache=False, pad=0.5, rect=True)[0] 87 | 88 | seen = 0 89 | names = model.names if hasattr(model, 'names') else model.module.names 90 | coco91class = coco80_to_coco91_class() 91 | s = ('%20s' + '%12s' * 6) % ('Class', 'Images', 'Targets', 'P', 'R', 'mAP@.5', 'mAP@.5:.95') 92 | p, r, f1, mp, mr, map50, map, t0, t1 = 0., 0., 0., 0., 0., 0., 0., 0., 0. 93 | loss = torch.zeros(3, device=device) 94 | jdict, stats, ap, ap_class = [], [], [], [] 95 | for batch_i, (img, targets, paths, shapes) in enumerate(tqdm(dataloader, desc=s)): 96 | img = img.to(device, non_blocking=True) 97 | img = img.half() if half else img.float() # uint8 to fp16/32 98 | img /= 255.0 # 0 - 255 to 0.0 - 1.0 99 | targets = targets.to(device) 100 | nb, _, height, width = img.shape # batch size, channels, height, width 101 | whwh = torch.Tensor([width, height, width, height]).to(device) 102 | 103 | # Disable gradients 104 | with torch.no_grad(): 105 | # Run model 106 | t = time_synchronized() 107 | inf_out, train_out = model(img, augment=augment) # inference and training outputs 108 | t0 += time_synchronized() - t 109 | 110 | # Compute loss 111 | if training: # if model has loss hyperparameters 112 | loss += compute_loss([x.float() for x in train_out], targets, model)[1][:3] # box, obj, cls 113 | 114 | # Run NMS 115 | t = time_synchronized() 116 | output = non_max_suppression(inf_out, conf_thres=conf_thres, iou_thres=iou_thres) 117 | t1 += time_synchronized() - t 118 | 119 | # Statistics per image 120 | for si, pred in enumerate(output): 121 | labels = targets[targets[:, 0] == si, 1:] 122 | nl = len(labels) 123 | tcls = labels[:, 0].tolist() if nl else [] # target class 124 | seen += 1 125 | 126 | if pred is None: 127 | if nl: 128 | stats.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls)) 129 | continue 130 | 131 | # Append to text file 132 | if save_txt: 133 | gn = torch.tensor(shapes[si][0])[[1, 0, 1, 0]] # normalization gain whwh 134 | x = pred.clone() 135 | x[:, :4] = scale_coords(img[si].shape[1:], x[:, :4], shapes[si][0], shapes[si][1]) # to original 136 | for *xyxy, conf, cls in x: 137 | xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh 138 | line = (cls, conf, *xywh) if save_conf else (cls, *xywh) # label format 139 | with open(str(out / Path(paths[si]).stem) + '.txt', 'a') as f: 140 | f.write(('%g ' * len(line) + '\n') % line) 141 | 142 | # Clip boxes to image bounds 143 | clip_coords(pred, (height, width)) 144 | 145 | # Append to pycocotools JSON dictionary 146 | if save_json: 147 | # [{"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}, ... 148 | image_id = Path(paths[si]).stem 149 | box = pred[:, :4].clone() # xyxy 150 | scale_coords(img[si].shape[1:], box, shapes[si][0], shapes[si][1]) # to original shape 151 | box = xyxy2xywh(box) # xywh 152 | box[:, :2] -= box[:, 2:] / 2 # xy center to top-left corner 153 | for p, b in zip(pred.tolist(), box.tolist()): 154 | jdict.append({'image_id': int(image_id) if image_id.isnumeric() else image_id, 155 | 'category_id': coco91class[int(p[5])], 156 | 'bbox': [round(x, 3) for x in b], 157 | 'score': round(p[4], 5)}) 158 | 159 | # Assign all predictions as incorrect 160 | correct = torch.zeros(pred.shape[0], niou, dtype=torch.bool, device=device) 161 | if nl: 162 | detected = [] # target indices 163 | tcls_tensor = labels[:, 0] 164 | 165 | # target boxes 166 | tbox = xywh2xyxy(labels[:, 1:5]) * whwh 167 | 168 | # Per target class 169 | for cls in torch.unique(tcls_tensor): 170 | ti = (cls == tcls_tensor).nonzero(as_tuple=False).view(-1) # prediction indices 171 | pi = (cls == pred[:, 5]).nonzero(as_tuple=False).view(-1) # target indices 172 | 173 | # Search for detections 174 | if pi.shape[0]: 175 | # Prediction to target ious 176 | ious, i = box_iou(pred[pi, :4], tbox[ti]).max(1) # best ious, indices 177 | 178 | # Append detections 179 | detected_set = set() 180 | for j in (ious > iouv[0]).nonzero(as_tuple=False): 181 | d = ti[i[j]] # detected target 182 | if d.item() not in detected_set: 183 | detected_set.add(d.item()) 184 | detected.append(d) 185 | correct[pi[j]] = ious[j] > iouv # iou_thres is 1xn 186 | if len(detected) == nl: # all targets already located in image 187 | break 188 | 189 | # Append statistics (correct, conf, pcls, tcls) 190 | stats.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls)) 191 | 192 | # Plot images 193 | if plots and batch_i < 1: 194 | f = save_dir / f'test_batch{batch_i}_gt.jpg' # filename 195 | plot_images(img, targets, paths, str(f), names) # ground truth 196 | f = save_dir / f'test_batch{batch_i}_pred.jpg' 197 | plot_images(img, output_to_target(output, width, height), paths, str(f), names) # predictions 198 | 199 | # Compute statistics 200 | stats = [np.concatenate(x, 0) for x in zip(*stats)] # to numpy 201 | if len(stats) and stats[0].any(): 202 | p, r, ap, f1, ap_class = ap_per_class(*stats, plot=plots, fname=save_dir / 'precision-recall_curve.png') 203 | p, r, ap50, ap = p[:, 0], r[:, 0], ap[:, 0], ap.mean(1) # [P, R, AP@0.5, AP@0.5:0.95] 204 | mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean() 205 | nt = np.bincount(stats[3].astype(np.int64), minlength=nc) # number of targets per class 206 | else: 207 | nt = torch.zeros(1) 208 | 209 | # Print results 210 | pf = '%20s' + '%12.3g' * 6 # print format 211 | print(pf % ('all', seen, nt.sum(), mp, mr, map50, map)) 212 | 213 | # Print results per class 214 | if verbose and nc > 1 and len(stats): 215 | for i, c in enumerate(ap_class): 216 | print(pf % (names[c], seen, nt[c], p[i], r[i], ap50[i], ap[i])) 217 | 218 | # Print speeds 219 | t = tuple(x / seen * 1E3 for x in (t0, t1, t0 + t1)) + (imgsz, imgsz, batch_size) # tuple 220 | if not training: 221 | print('Speed: %.1f/%.1f/%.1f ms inference/NMS/total per %gx%g image at batch-size %g' % t) 222 | 223 | # Save JSON 224 | if save_json and len(jdict): 225 | w = Path(weights[0] if isinstance(weights, list) else weights).stem if weights is not None else '' # weights 226 | file = save_dir / f"detections_val2017_{w}_results.json" # predicted annotations file 227 | print('\nCOCO mAP with pycocotools... saving %s...' % file) 228 | with open(file, 'w') as f: 229 | json.dump(jdict, f) 230 | 231 | try: # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb 232 | from pycocotools.coco import COCO 233 | from pycocotools.cocoeval import COCOeval 234 | 235 | imgIds = [int(Path(x).stem) for x in dataloader.dataset.img_files] 236 | cocoGt = COCO(glob.glob('../coco/annotations/instances_val*.json')[0]) # initialize COCO ground truth api 237 | cocoDt = cocoGt.loadRes(str(file)) # initialize COCO pred api 238 | cocoEval = COCOeval(cocoGt, cocoDt, 'bbox') 239 | cocoEval.params.imgIds = imgIds # image IDs to evaluate 240 | cocoEval.evaluate() 241 | cocoEval.accumulate() 242 | cocoEval.summarize() 243 | map, map50 = cocoEval.stats[:2] # update results (mAP@0.5:0.95, mAP@0.5) 244 | except Exception as e: 245 | print('ERROR: pycocotools unable to run: %s' % e) 246 | 247 | # Return results 248 | model.float() # for training 249 | maps = np.zeros(nc) + map 250 | for i, c in enumerate(ap_class): 251 | maps[c] = ap[i] 252 | return (mp, mr, map50, map, *(loss.cpu() / len(dataloader)).tolist()), maps, t 253 | 254 | 255 | if __name__ == '__main__': 256 | parser = argparse.ArgumentParser(prog='test.py') 257 | parser.add_argument('--weights', nargs='+', type=str, default='yolov5s.pt', help='model.pt path(s)') 258 | parser.add_argument('--data', type=str, default='data/coco128.yaml', help='*.data path') 259 | parser.add_argument('--batch-size', type=int, default=32, help='size of each image batch') 260 | parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)') 261 | parser.add_argument('--conf-thres', type=float, default=0.001, help='object confidence threshold') 262 | parser.add_argument('--iou-thres', type=float, default=0.65, help='IOU threshold for NMS') 263 | parser.add_argument('--save-json', action='store_true', help='save a cocoapi-compatible JSON results file') 264 | parser.add_argument('--task', default='val', help="'val', 'test', 'study'") 265 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 266 | parser.add_argument('--single-cls', action='store_true', help='treat as single-class dataset') 267 | parser.add_argument('--augment', action='store_true', help='augmented inference') 268 | parser.add_argument('--verbose', action='store_true', help='report mAP by class') 269 | parser.add_argument('--save-txt', action='store_true', help='save results to *.txt') 270 | parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels') 271 | parser.add_argument('--save-dir', type=str, default='runs/test', help='directory to save results') 272 | opt = parser.parse_args() 273 | opt.save_json |= opt.data.endswith('coco.yaml') 274 | opt.data = check_file(opt.data) # check file 275 | print(opt) 276 | 277 | if opt.task in ['val', 'test']: # run normally 278 | test(opt.data, 279 | opt.weights, 280 | opt.batch_size, 281 | opt.img_size, 282 | opt.conf_thres, 283 | opt.iou_thres, 284 | opt.save_json, 285 | opt.single_cls, 286 | opt.augment, 287 | opt.verbose, 288 | save_dir=Path(opt.save_dir), 289 | save_txt=opt.save_txt, 290 | save_conf=opt.save_conf, 291 | ) 292 | 293 | print('Results saved to %s' % opt.save_dir) 294 | 295 | elif opt.task == 'study': # run over a range of settings and save/plot 296 | for weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt']: 297 | f = 'study_%s_%s.txt' % (Path(opt.data).stem, Path(weights).stem) # filename to save to 298 | x = list(range(320, 800, 64)) # x axis 299 | y = [] # y axis 300 | for i in x: # img-size 301 | print('\nRunning %s point %s...' % (f, i)) 302 | r, _, t = test(opt.data, weights, opt.batch_size, i, opt.conf_thres, opt.iou_thres, opt.save_json) 303 | y.append(r + t) # results and times 304 | np.savetxt(f, y, fmt='%10.4g') # save 305 | os.system('zip -r study.zip study_*.txt') 306 | # utils.general.plot_study_txt(f, x) # plot 307 | -------------------------------------------------------------------------------- /YOLOv5/train.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import logging 3 | import os 4 | import random 5 | import shutil 6 | import time 7 | from pathlib import Path 8 | from warnings import warn 9 | 10 | import math 11 | import numpy as np 12 | import torch.distributed as dist 13 | import torch.nn.functional as F 14 | import torch.optim as optim 15 | import torch.optim.lr_scheduler as lr_scheduler 16 | import torch.utils.data 17 | import yaml 18 | from torch.cuda import amp 19 | from torch.nn.parallel import DistributedDataParallel as DDP 20 | from torch.utils.tensorboard import SummaryWriter 21 | from tqdm import tqdm 22 | 23 | import test # import test.py to get mAP after each epoch 24 | from models.yolo import Model 25 | from utils.datasets import create_dataloader 26 | from utils.general import ( 27 | torch_distributed_zero_first, labels_to_class_weights, plot_labels, check_anchors, labels_to_image_weights, 28 | compute_loss, plot_images, fitness, strip_optimizer, plot_results, get_latest_run, check_dataset, check_file, 29 | check_git_status, check_img_size, increment_dir, print_mutation, plot_evolution, set_logging, init_seeds) 30 | from utils.google_utils import attempt_download 31 | from utils.torch_utils import ModelEMA, select_device, intersect_dicts 32 | 33 | logger = logging.getLogger(__name__) 34 | 35 | 36 | def train(hyp, opt, device, tb_writer=None): 37 | logger.info(f'Hyperparameters {hyp}') 38 | log_dir = Path(tb_writer.log_dir) if tb_writer else Path(opt.logdir) / 'evolve' # logging directory 39 | wdir = log_dir / 'weights' # weights directory 40 | os.makedirs(wdir, exist_ok=True) 41 | last = wdir / 'last.pt' 42 | best = wdir / 'best.pt' 43 | results_file = str(log_dir / 'results.txt') 44 | epochs, batch_size, total_batch_size, weights, rank = \ 45 | opt.epochs, opt.batch_size, opt.total_batch_size, opt.weights, opt.global_rank 46 | 47 | # Save run settings 48 | with open(log_dir / 'hyp.yaml', 'w') as f: 49 | yaml.dump(hyp, f, sort_keys=False) 50 | with open(log_dir / 'opt.yaml', 'w') as f: 51 | yaml.dump(vars(opt), f, sort_keys=False) 52 | 53 | # Configure 54 | cuda = device.type != 'cpu' 55 | init_seeds(2 + rank) 56 | with open(opt.data) as f: 57 | data_dict = yaml.load(f, Loader=yaml.FullLoader) # data dict 58 | with torch_distributed_zero_first(rank): 59 | check_dataset(data_dict) # check 60 | train_path = data_dict['train'] 61 | test_path = data_dict['val'] 62 | nc, names = (1, ['item']) if opt.single_cls else (int(data_dict['nc']), data_dict['names']) # number classes, names 63 | assert len(names) == nc, '%g names found for nc=%g dataset in %s' % (len(names), nc, opt.data) # check 64 | 65 | # Model 66 | pretrained = weights.endswith('.pt') 67 | if pretrained: 68 | with torch_distributed_zero_first(rank): 69 | attempt_download(weights) # download if not found locally 70 | ckpt = torch.load(weights, map_location=device) # load checkpoint 71 | if hyp.get('anchors'): 72 | ckpt['model'].yaml['anchors'] = round(hyp['anchors']) # force autoanchor 73 | model = Model(opt.cfg or ckpt['model'].yaml, ch=3, nc=nc).to(device) # create 74 | exclude = ['anchor'] if opt.cfg or hyp.get('anchors') else [] # exclude keys 75 | state_dict = ckpt['model'].float().state_dict() # to FP32 76 | state_dict = intersect_dicts(state_dict, model.state_dict(), exclude=exclude) # intersect 77 | model.load_state_dict(state_dict, strict=False) # load 78 | logger.info('Transferred %g/%g items from %s' % (len(state_dict), len(model.state_dict()), weights)) # report 79 | else: 80 | model = Model(opt.cfg, ch=3, nc=nc).to(device) # create 81 | 82 | # Freeze 83 | freeze = ['', ] # parameter names to freeze (full or partial) 84 | if any(freeze): 85 | for k, v in model.named_parameters(): 86 | if any(x in k for x in freeze): 87 | print('freezing %s' % k) 88 | v.requires_grad = False 89 | 90 | # Optimizer 91 | nbs = 64 # nominal batch size 92 | accumulate = max(round(nbs / total_batch_size), 1) # accumulate loss before optimizing 93 | hyp['weight_decay'] *= total_batch_size * accumulate / nbs # scale weight_decay 94 | 95 | pg0, pg1, pg2 = [], [], [] # optimizer parameter groups 96 | for k, v in model.named_parameters(): 97 | v.requires_grad = True 98 | if '.bias' in k: 99 | pg2.append(v) # biases 100 | elif '.weight' in k and '.bn' not in k: 101 | pg1.append(v) # apply weight decay 102 | else: 103 | pg0.append(v) # all else 104 | 105 | if opt.adam: 106 | optimizer = optim.Adam(pg0, lr=hyp['lr0'], betas=(hyp['momentum'], 0.999)) # adjust beta1 to momentum 107 | else: 108 | optimizer = optim.SGD(pg0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True) 109 | 110 | optimizer.add_param_group({'params': pg1, 'weight_decay': hyp['weight_decay']}) # add pg1 with weight_decay 111 | optimizer.add_param_group({'params': pg2}) # add pg2 (biases) 112 | logger.info('Optimizer groups: %g .bias, %g conv.weight, %g other' % (len(pg2), len(pg1), len(pg0))) 113 | del pg0, pg1, pg2 114 | 115 | # Scheduler https://arxiv.org/pdf/1812.01187.pdf 116 | # https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html#OneCycleLR 117 | lf = lambda x: ((1 + math.cos(x * math.pi / epochs)) / 2) * (1 - hyp['lrf']) + hyp['lrf'] # cosine 118 | scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) 119 | # plot_lr_scheduler(optimizer, scheduler, epochs) 120 | 121 | # Resume 122 | start_epoch, best_fitness = 0, 0.0 123 | if pretrained: 124 | # Optimizer 125 | if ckpt['optimizer'] is not None: 126 | optimizer.load_state_dict(ckpt['optimizer']) 127 | best_fitness = ckpt['best_fitness'] 128 | 129 | # Results 130 | if ckpt.get('training_results') is not None: 131 | with open(results_file, 'w') as file: 132 | file.write(ckpt['training_results']) # write results.txt 133 | 134 | # Epochs 135 | start_epoch = ckpt['epoch'] + 1 136 | if opt.resume: 137 | assert start_epoch > 0, '%s training to %g epochs is finished, nothing to resume.' % (weights, epochs) 138 | shutil.copytree(wdir, wdir.parent / f'weights_backup_epoch{start_epoch - 1}') # save previous weights 139 | if epochs < start_epoch: 140 | logger.info('%s has been trained for %g epochs. Fine-tuning for %g additional epochs.' % 141 | (weights, ckpt['epoch'], epochs)) 142 | epochs += ckpt['epoch'] # finetune additional epochs 143 | 144 | del ckpt, state_dict 145 | 146 | # Image sizes 147 | gs = int(max(model.stride)) # grid size (max stride) 148 | imgsz, imgsz_test = [check_img_size(x, gs) for x in opt.img_size] # verify imgsz are gs-multiples 149 | 150 | # DP mode 151 | if cuda and rank == -1 and torch.cuda.device_count() > 1: 152 | model = torch.nn.DataParallel(model) 153 | 154 | # SyncBatchNorm 155 | if opt.sync_bn and cuda and rank != -1: 156 | model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device) 157 | logger.info('Using SyncBatchNorm()') 158 | 159 | # Exponential moving average 160 | ema = ModelEMA(model) if rank in [-1, 0] else None 161 | 162 | # DDP mode 163 | if cuda and rank != -1: 164 | model = DDP(model, device_ids=[opt.local_rank], output_device=opt.local_rank) 165 | 166 | # Trainloader 167 | dataloader, dataset = create_dataloader(train_path, imgsz, batch_size, gs, opt, 168 | hyp=hyp, augment=True, cache=opt.cache_images, rect=opt.rect, 169 | rank=rank, world_size=opt.world_size, workers=opt.workers) 170 | mlc = np.concatenate(dataset.labels, 0)[:, 0].max() # max label class 171 | nb = len(dataloader) # number of batches 172 | assert mlc < nc, 'Label class %g exceeds nc=%g in %s. Possible class labels are 0-%g' % (mlc, nc, opt.data, nc - 1) 173 | 174 | # Process 0 175 | if rank in [-1, 0]: 176 | ema.updates = start_epoch * nb // accumulate # set EMA updates 177 | testloader = create_dataloader(test_path, imgsz_test, total_batch_size, gs, opt, 178 | hyp=hyp, augment=False, cache=opt.cache_images and not opt.notest, rect=True, 179 | rank=-1, world_size=opt.world_size, workers=opt.workers)[0] # testloader 180 | 181 | if not opt.resume: 182 | labels = np.concatenate(dataset.labels, 0) 183 | c = torch.tensor(labels[:, 0]) # classes 184 | # cf = torch.bincount(c.long(), minlength=nc) + 1. # frequency 185 | # model._initialize_biases(cf.to(device)) 186 | plot_labels(labels, save_dir=log_dir) 187 | if tb_writer: 188 | # tb_writer.add_hparams(hyp, {}) # causes duplicate https://github.com/ultralytics/yolov5/pull/384 189 | tb_writer.add_histogram('classes', c, 0) 190 | 191 | # Anchors 192 | if not opt.noautoanchor: 193 | check_anchors(dataset, model=model, thr=hyp['anchor_t'], imgsz=imgsz) 194 | 195 | # Model parameters 196 | hyp['cls'] *= nc / 80. # scale coco-tuned hyp['cls'] to current dataset 197 | model.nc = nc # attach number of classes to model 198 | model.hyp = hyp # attach hyperparameters to model 199 | model.gr = 1.0 # iou loss ratio (obj_loss = 1.0 or iou) 200 | model.class_weights = labels_to_class_weights(dataset.labels, nc).to(device) # attach class weights 201 | model.names = names 202 | 203 | # Start training 204 | t0 = time.time() 205 | nw = max(round(hyp['warmup_epochs'] * nb), 1e3) # number of warmup iterations, max(3 epochs, 1k iterations) 206 | # nw = min(nw, (epochs - start_epoch) / 2 * nb) # limit warmup to < 1/2 of training 207 | maps = np.zeros(nc) # mAP per class 208 | results = (0, 0, 0, 0, 0, 0, 0) # P, R, mAP@.5, mAP@.5-.95, val_loss(box, obj, cls) 209 | scheduler.last_epoch = start_epoch - 1 # do not move 210 | scaler = amp.GradScaler(enabled=cuda) 211 | logger.info('Image sizes %g train, %g test\n' 212 | 'Using %g dataloader workers\nLogging results to %s\n' 213 | 'Starting training for %g epochs...' % (imgsz, imgsz_test, dataloader.num_workers, log_dir, epochs)) 214 | for epoch in range(start_epoch, epochs): # epoch ------------------------------------------------------------------ 215 | model.train() 216 | 217 | # Update image weights (optional) 218 | if opt.image_weights: 219 | # Generate indices 220 | if rank in [-1, 0]: 221 | cw = model.class_weights.cpu().numpy() * (1 - maps) ** 2 # class weights 222 | iw = labels_to_image_weights(dataset.labels, nc=nc, class_weights=cw) # image weights 223 | dataset.indices = random.choices(range(dataset.n), weights=iw, k=dataset.n) # rand weighted idx 224 | # Broadcast if DDP 225 | if rank != -1: 226 | indices = (torch.tensor(dataset.indices) if rank == 0 else torch.zeros(dataset.n)).int() 227 | dist.broadcast(indices, 0) 228 | if rank != 0: 229 | dataset.indices = indices.cpu().numpy() 230 | 231 | # Update mosaic border 232 | # b = int(random.uniform(0.25 * imgsz, 0.75 * imgsz + gs) // gs * gs) 233 | # dataset.mosaic_border = [b - imgsz, -b] # height, width borders 234 | 235 | mloss = torch.zeros(4, device=device) # mean losses 236 | if rank != -1: 237 | dataloader.sampler.set_epoch(epoch) 238 | pbar = enumerate(dataloader) 239 | logger.info(('\n' + '%10s' * 8) % ('Epoch', 'gpu_mem', 'box', 'obj', 'cls', 'total', 'targets', 'img_size')) 240 | if rank in [-1, 0]: 241 | pbar = tqdm(pbar, total=nb) # progress bar 242 | optimizer.zero_grad() 243 | for i, (imgs, targets, paths, _) in pbar: # batch ------------------------------------------------------------- 244 | ni = i + nb * epoch # number integrated batches (since train start) 245 | imgs = imgs.to(device, non_blocking=True).float() / 255.0 # uint8 to float32, 0-255 to 0.0-1.0 246 | 247 | # Warmup 248 | if ni <= nw: 249 | xi = [0, nw] # x interp 250 | # model.gr = np.interp(ni, xi, [0.0, 1.0]) # iou loss ratio (obj_loss = 1.0 or iou) 251 | accumulate = max(1, np.interp(ni, xi, [1, nbs / total_batch_size]).round()) 252 | for j, x in enumerate(optimizer.param_groups): 253 | # bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0 254 | x['lr'] = np.interp(ni, xi, [hyp['warmup_bias_lr'] if j == 2 else 0.0, x['initial_lr'] * lf(epoch)]) 255 | if 'momentum' in x: 256 | x['momentum'] = np.interp(ni, xi, [hyp['warmup_momentum'], hyp['momentum']]) 257 | 258 | # Multi-scale 259 | if opt.multi_scale: 260 | sz = random.randrange(imgsz * 0.5, imgsz * 1.5 + gs) // gs * gs # size 261 | sf = sz / max(imgs.shape[2:]) # scale factor 262 | if sf != 1: 263 | ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]] # new shape (stretched to gs-multiple) 264 | imgs = F.interpolate(imgs, size=ns, mode='bilinear', align_corners=False) 265 | 266 | # Forward 267 | with amp.autocast(enabled=cuda): 268 | pred = model(imgs) # forward 269 | loss, loss_items = compute_loss(pred, targets.to(device), model) # loss scaled by batch_size 270 | if rank != -1: 271 | loss *= opt.world_size # gradient averaged between devices in DDP mode 272 | 273 | # Backward 274 | scaler.scale(loss).backward() 275 | 276 | # Optimize 277 | if ni % accumulate == 0: 278 | scaler.step(optimizer) # optimizer.step 279 | scaler.update() 280 | optimizer.zero_grad() 281 | if ema: 282 | ema.update(model) 283 | 284 | # Print 285 | if rank in [-1, 0]: 286 | mloss = (mloss * i + loss_items) / (i + 1) # update mean losses 287 | mem = '%.3gG' % (torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0) # (GB) 288 | s = ('%10s' * 2 + '%10.4g' * 6) % ( 289 | '%g/%g' % (epoch, epochs - 1), mem, *mloss, targets.shape[0], imgs.shape[-1]) 290 | pbar.set_description(s) 291 | 292 | # Plot 293 | if ni < 3: 294 | f = str(log_dir / f'train_batch{ni}.jpg') # filename 295 | result = plot_images(images=imgs, targets=targets, paths=paths, fname=f) 296 | # if tb_writer and result is not None: 297 | # tb_writer.add_image(f, result, dataformats='HWC', global_step=epoch) 298 | # tb_writer.add_graph(model, imgs) # add model to tensorboard 299 | 300 | # end batch ------------------------------------------------------------------------------------------------ 301 | 302 | # Scheduler 303 | lr = [x['lr'] for x in optimizer.param_groups] # for tensorboard 304 | scheduler.step() 305 | 306 | # DDP process 0 or single-GPU 307 | if rank in [-1, 0]: 308 | # mAP 309 | if ema: 310 | ema.update_attr(model, include=['yaml', 'nc', 'hyp', 'gr', 'names', 'stride']) 311 | final_epoch = epoch + 1 == epochs 312 | if not opt.notest or final_epoch: # Calculate mAP 313 | results, maps, times = test.test(opt.data, 314 | batch_size=total_batch_size, 315 | imgsz=imgsz_test, 316 | model=ema.ema, 317 | single_cls=opt.single_cls, 318 | dataloader=testloader, 319 | save_dir=log_dir, 320 | plots=epoch == 0 or final_epoch) # plot first and last 321 | 322 | # Write 323 | with open(results_file, 'a') as f: 324 | f.write(s + '%10.4g' * 7 % results + '\n') # P, R, mAP@.5, mAP@.5-.95, val_loss(box, obj, cls) 325 | if len(opt.name) and opt.bucket: 326 | os.system('gsutil cp %s gs://%s/results/results%s.txt' % (results_file, opt.bucket, opt.name)) 327 | 328 | # Tensorboard 329 | if tb_writer: 330 | tags = ['train/box_loss', 'train/obj_loss', 'train/cls_loss', # train loss 331 | 'metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95', 332 | 'val/box_loss', 'val/obj_loss', 'val/cls_loss', # val loss 333 | 'x/lr0', 'x/lr1', 'x/lr2'] # params 334 | for x, tag in zip(list(mloss[:-1]) + list(results) + lr, tags): 335 | tb_writer.add_scalar(tag, x, epoch) 336 | 337 | # Update best mAP 338 | fi = fitness(np.array(results).reshape(1, -1)) # weighted combination of [P, R, mAP@.5, mAP@.5-.95] 339 | if fi > best_fitness: 340 | best_fitness = fi 341 | 342 | # Save model 343 | save = (not opt.nosave) or (final_epoch and not opt.evolve) 344 | if save: 345 | with open(results_file, 'r') as f: # create checkpoint 346 | ckpt = {'epoch': epoch, 347 | 'best_fitness': best_fitness, 348 | 'training_results': f.read(), 349 | 'model': ema.ema, 350 | 'optimizer': None if final_epoch else optimizer.state_dict()} 351 | 352 | # Save last, best and delete 353 | torch.save(ckpt, last) 354 | if best_fitness == fi: 355 | torch.save(ckpt, best) 356 | del ckpt 357 | # end epoch ---------------------------------------------------------------------------------------------------- 358 | # end training 359 | 360 | if rank in [-1, 0]: 361 | # Strip optimizers 362 | n = opt.name if opt.name.isnumeric() else '' 363 | fresults, flast, fbest = log_dir / f'results{n}.txt', wdir / f'last{n}.pt', wdir / f'best{n}.pt' 364 | for f1, f2 in zip([wdir / 'last.pt', wdir / 'best.pt', results_file], [flast, fbest, fresults]): 365 | if os.path.exists(f1): 366 | os.rename(f1, f2) # rename 367 | if str(f2).endswith('.pt'): # is *.pt 368 | strip_optimizer(f2) # strip optimizer 369 | os.system('gsutil cp %s gs://%s/weights' % (f2, opt.bucket)) if opt.bucket else None # upload 370 | # Finish 371 | if not opt.evolve: 372 | plot_results(save_dir=log_dir) # save as results.png 373 | logger.info('%g epochs completed in %.3f hours.\n' % (epoch - start_epoch + 1, (time.time() - t0) / 3600)) 374 | 375 | dist.destroy_process_group() if rank not in [-1, 0] else None 376 | torch.cuda.empty_cache() 377 | return results 378 | 379 | 380 | if __name__ == '__main__': 381 | parser = argparse.ArgumentParser() 382 | parser.add_argument('--weights', type=str, default='yolov5s.pt', help='initial weights path') 383 | parser.add_argument('--cfg', type=str, default='', help='model.yaml path') 384 | parser.add_argument('--data', type=str, default='data/coco128.yaml', help='data.yaml path') 385 | parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path') 386 | parser.add_argument('--epochs', type=int, default=300) 387 | parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs') 388 | parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes') 389 | parser.add_argument('--rect', action='store_true', help='rectangular training') 390 | parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training') 391 | parser.add_argument('--nosave', action='store_true', help='only save final checkpoint') 392 | parser.add_argument('--notest', action='store_true', help='only test final epoch') 393 | parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check') 394 | parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters') 395 | parser.add_argument('--bucket', type=str, default='', help='gsutil bucket') 396 | parser.add_argument('--cache-images', action='store_true', help='cache images for faster training') 397 | parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training') 398 | parser.add_argument('--name', default='', help='renames experiment folder exp{N} to exp{N}_{name} if supplied') 399 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 400 | parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%') 401 | parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset') 402 | parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer') 403 | parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode') 404 | parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify') 405 | parser.add_argument('--logdir', type=str, default='runs/', help='logging directory') 406 | parser.add_argument('--workers', type=int, default=4, help='maximum number of dataloader workers') 407 | opt = parser.parse_args() 408 | 409 | # Set DDP variables 410 | opt.total_batch_size = opt.batch_size 411 | opt.world_size = int(os.environ['WORLD_SIZE']) if 'WORLD_SIZE' in os.environ else 1 412 | opt.global_rank = int(os.environ['RANK']) if 'RANK' in os.environ else -1 413 | set_logging(opt.global_rank) 414 | if opt.global_rank in [-1, 0]: 415 | check_git_status() 416 | 417 | # Resume 418 | if opt.resume: # resume an interrupted run 419 | ckpt = opt.resume if isinstance(opt.resume, str) else get_latest_run() # specified or most recent path 420 | log_dir = Path(ckpt).parent.parent # runs/exp0 421 | assert os.path.isfile(ckpt), 'ERROR: --resume checkpoint does not exist' 422 | with open(log_dir / 'opt.yaml') as f: 423 | opt = argparse.Namespace(**yaml.load(f, Loader=yaml.FullLoader)) # replace 424 | opt.cfg, opt.weights, opt.resume = '', ckpt, True 425 | logger.info('Resuming training from %s' % ckpt) 426 | 427 | else: 428 | # opt.hyp = opt.hyp or ('hyp.finetune.yaml' if opt.weights else 'hyp.scratch.yaml') 429 | opt.data, opt.cfg, opt.hyp = check_file(opt.data), check_file(opt.cfg), check_file(opt.hyp) # check files 430 | assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified' 431 | opt.img_size.extend([opt.img_size[-1]] * (2 - len(opt.img_size))) # extend to 2 sizes (train, test) 432 | log_dir = increment_dir(Path(opt.logdir) / 'exp', opt.name) # runs/exp1 433 | 434 | # DDP mode 435 | device = select_device(opt.device, batch_size=opt.batch_size) 436 | if opt.local_rank != -1: 437 | assert torch.cuda.device_count() > opt.local_rank 438 | torch.cuda.set_device(opt.local_rank) 439 | device = torch.device('cuda', opt.local_rank) 440 | dist.init_process_group(backend='nccl', init_method='env://') # distributed backend 441 | assert opt.batch_size % opt.world_size == 0, '--batch-size must be multiple of CUDA device count' 442 | opt.batch_size = opt.total_batch_size // opt.world_size 443 | 444 | # Hyperparameters 445 | with open(opt.hyp) as f: 446 | hyp = yaml.load(f, Loader=yaml.FullLoader) # load hyps 447 | if 'box' not in hyp: 448 | warn('Compatibility: %s missing "box" which was renamed from "giou" in %s' % 449 | (opt.hyp, 'https://github.com/ultralytics/yolov5/pull/1120')) 450 | hyp['box'] = hyp.pop('giou') 451 | 452 | # Train 453 | logger.info(opt) 454 | if not opt.evolve: 455 | tb_writer = None 456 | if opt.global_rank in [-1, 0]: 457 | logger.info(f'Start Tensorboard with "tensorboard --logdir {opt.logdir}", view at http://localhost:6006/') 458 | tb_writer = SummaryWriter(log_dir=log_dir) # runs/exp0 459 | 460 | train(hyp, opt, device, tb_writer) 461 | 462 | # Evolve hyperparameters (optional) 463 | else: 464 | # Hyperparameter evolution metadata (mutation scale 0-1, lower_limit, upper_limit) 465 | meta = {'lr0': (1, 1e-5, 1e-1), # initial learning rate (SGD=1E-2, Adam=1E-3) 466 | 'lrf': (1, 0.01, 1.0), # final OneCycleLR learning rate (lr0 * lrf) 467 | 'momentum': (0.3, 0.6, 0.98), # SGD momentum/Adam beta1 468 | 'weight_decay': (1, 0.0, 0.001), # optimizer weight decay 469 | 'warmup_epochs': (1, 0.0, 5.0), # warmup epochs (fractions ok) 470 | 'warmup_momentum': (1, 0.0, 0.95), # warmup initial momentum 471 | 'warmup_bias_lr': (1, 0.0, 0.2), # warmup initial bias lr 472 | 'box': (1, 0.02, 0.2), # box loss gain 473 | 'cls': (1, 0.2, 4.0), # cls loss gain 474 | 'cls_pw': (1, 0.5, 2.0), # cls BCELoss positive_weight 475 | 'obj': (1, 0.2, 4.0), # obj loss gain (scale with pixels) 476 | 'obj_pw': (1, 0.5, 2.0), # obj BCELoss positive_weight 477 | 'iou_t': (0, 0.1, 0.7), # IoU training threshold 478 | 'anchor_t': (1, 2.0, 8.0), # anchor-multiple threshold 479 | 'anchors': (2, 2.0, 10.0), # anchors per output grid (0 to ignore) 480 | 'fl_gamma': (0, 0.0, 2.0), # focal loss gamma (efficientDet default gamma=1.5) 481 | 'hsv_h': (1, 0.0, 0.1), # image HSV-Hue augmentation (fraction) 482 | 'hsv_s': (1, 0.0, 0.9), # image HSV-Saturation augmentation (fraction) 483 | 'hsv_v': (1, 0.0, 0.9), # image HSV-Value augmentation (fraction) 484 | 'degrees': (1, 0.0, 45.0), # image rotation (+/- deg) 485 | 'translate': (1, 0.0, 0.9), # image translation (+/- fraction) 486 | 'scale': (1, 0.0, 0.9), # image scale (+/- gain) 487 | 'shear': (1, 0.0, 10.0), # image shear (+/- deg) 488 | 'perspective': (0, 0.0, 0.001), # image perspective (+/- fraction), range 0-0.001 489 | 'flipud': (1, 0.0, 1.0), # image flip up-down (probability) 490 | 'fliplr': (0, 0.0, 1.0), # image flip left-right (probability) 491 | 'mosaic': (1, 0.0, 1.0), # image mixup (probability) 492 | 'mixup': (1, 0.0, 1.0)} # image mixup (probability) 493 | 494 | assert opt.local_rank == -1, 'DDP mode not implemented for --evolve' 495 | opt.notest, opt.nosave = True, True # only test/save final epoch 496 | # ei = [isinstance(x, (int, float)) for x in hyp.values()] # evolvable indices 497 | yaml_file = Path(opt.logdir) / 'evolve' / 'hyp_evolved.yaml' # save best result here 498 | if opt.bucket: 499 | os.system('gsutil cp gs://%s/evolve.txt .' % opt.bucket) # download evolve.txt if exists 500 | 501 | for _ in range(300): # generations to evolve 502 | if os.path.exists('evolve.txt'): # if evolve.txt exists: select best hyps and mutate 503 | # Select parent(s) 504 | parent = 'single' # parent selection method: 'single' or 'weighted' 505 | x = np.loadtxt('evolve.txt', ndmin=2) 506 | n = min(5, len(x)) # number of previous results to consider 507 | x = x[np.argsort(-fitness(x))][:n] # top n mutations 508 | w = fitness(x) - fitness(x).min() # weights 509 | if parent == 'single' or len(x) == 1: 510 | # x = x[random.randint(0, n - 1)] # random selection 511 | x = x[random.choices(range(n), weights=w)[0]] # weighted selection 512 | elif parent == 'weighted': 513 | x = (x * w.reshape(n, 1)).sum(0) / w.sum() # weighted combination 514 | 515 | # Mutate 516 | mp, s = 0.8, 0.2 # mutation probability, sigma 517 | npr = np.random 518 | npr.seed(int(time.time())) 519 | g = np.array([x[0] for x in meta.values()]) # gains 0-1 520 | ng = len(meta) 521 | v = np.ones(ng) 522 | while all(v == 1): # mutate until a change occurs (prevent duplicates) 523 | v = (g * (npr.random(ng) < mp) * npr.randn(ng) * npr.random() * s + 1).clip(0.3, 3.0) 524 | for i, k in enumerate(hyp.keys()): # plt.hist(v.ravel(), 300) 525 | hyp[k] = float(x[i + 7] * v[i]) # mutate 526 | 527 | # Constrain to limits 528 | for k, v in meta.items(): 529 | hyp[k] = max(hyp[k], v[1]) # lower limit 530 | hyp[k] = min(hyp[k], v[2]) # upper limit 531 | hyp[k] = round(hyp[k], 5) # significant digits 532 | 533 | # Train mutation 534 | results = train(hyp.copy(), opt, device) 535 | 536 | # Write mutation results 537 | print_mutation(hyp.copy(), results, yaml_file, opt.bucket) 538 | 539 | # Plot results 540 | plot_evolution(yaml_file) 541 | print(f'Hyperparameter evolution complete. Best results saved as: {yaml_file}\n' 542 | f'Command to train a new model with these hyperparameters: $ python train.py --hyp {yaml_file}') 543 | -------------------------------------------------------------------------------- /YOLOv5/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enigmaaiorg/RasPi-BloodView/8c354bfa17727c21ebe0b854a187586147a5a06a/YOLOv5/utils/__init__.py -------------------------------------------------------------------------------- /YOLOv5/utils/activations.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | 6 | # Swish https://arxiv.org/pdf/1905.02244.pdf --------------------------------------------------------------------------- 7 | class Swish(nn.Module): # 8 | @staticmethod 9 | def forward(x): 10 | return x * torch.sigmoid(x) 11 | 12 | 13 | class Hardswish(nn.Module): # export-friendly version of nn.Hardswish() 14 | @staticmethod 15 | def forward(x): 16 | # return x * F.hardsigmoid(x) # for torchscript and CoreML 17 | return x * F.hardtanh(x + 3, 0., 6.) / 6. # for torchscript, CoreML and ONNX 18 | 19 | 20 | class MemoryEfficientSwish(nn.Module): 21 | class F(torch.autograd.Function): 22 | @staticmethod 23 | def forward(ctx, x): 24 | ctx.save_for_backward(x) 25 | return x * torch.sigmoid(x) 26 | 27 | @staticmethod 28 | def backward(ctx, grad_output): 29 | x = ctx.saved_tensors[0] 30 | sx = torch.sigmoid(x) 31 | return grad_output * (sx * (1 + x * (1 - sx))) 32 | 33 | def forward(self, x): 34 | return self.F.apply(x) 35 | 36 | 37 | # Mish https://github.com/digantamisra98/Mish -------------------------------------------------------------------------- 38 | class Mish(nn.Module): 39 | @staticmethod 40 | def forward(x): 41 | return x * F.softplus(x).tanh() 42 | 43 | 44 | class MemoryEfficientMish(nn.Module): 45 | class F(torch.autograd.Function): 46 | @staticmethod 47 | def forward(ctx, x): 48 | ctx.save_for_backward(x) 49 | return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x))) 50 | 51 | @staticmethod 52 | def backward(ctx, grad_output): 53 | x = ctx.saved_tensors[0] 54 | sx = torch.sigmoid(x) 55 | fx = F.softplus(x).tanh() 56 | return grad_output * (fx + x * sx * (1 - fx * fx)) 57 | 58 | def forward(self, x): 59 | return self.F.apply(x) 60 | 61 | 62 | # FReLU https://arxiv.org/abs/2007.11824 ------------------------------------------------------------------------------- 63 | class FReLU(nn.Module): 64 | def __init__(self, c1, k=3): # ch_in, kernel 65 | super().__init__() 66 | self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1) 67 | self.bn = nn.BatchNorm2d(c1) 68 | 69 | def forward(self, x): 70 | return torch.max(x, self.bn(self.conv(x))) 71 | -------------------------------------------------------------------------------- /YOLOv5/utils/evolve.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Hyperparameter evolution commands (avoids CUDA memory leakage issues) 3 | # Replaces train.py python generations 'for' loop with a bash 'for' loop 4 | 5 | # Start on 4-GPU machine 6 | #for i in 0 1 2 3; do 7 | # t=ultralytics/yolov5:evolve && sudo docker pull $t && sudo docker run -d --ipc=host --gpus all -v "$(pwd)"/VOC:/usr/src/VOC $t bash utils/evolve.sh $i 8 | # sleep 60 # avoid simultaneous evolve.txt read/write 9 | #done 10 | 11 | # Hyperparameter evolution commands 12 | while true; do 13 | # python train.py --batch 64 --weights yolov5m.pt --data voc.yaml --img 512 --epochs 50 --evolve --bucket ult/evolve/voc --device $1 14 | python train.py --batch 40 --weights yolov5m.pt --data coco.yaml --img 640 --epochs 30 --evolve --bucket ult/evolve/coco --device $1 15 | done 16 | -------------------------------------------------------------------------------- /YOLOv5/utils/google_app_engine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/google-appengine/python 2 | 3 | # Create a virtualenv for dependencies. This isolates these packages from 4 | # system-level packages. 5 | # Use -p python3 or -p python3.7 to select python version. Default is version 2. 6 | RUN virtualenv /env -p python3 7 | 8 | # Setting these environment variables are the same as running 9 | # source /env/bin/activate. 10 | ENV VIRTUAL_ENV /env 11 | ENV PATH /env/bin:$PATH 12 | 13 | RUN apt-get update && apt-get install -y python-opencv 14 | 15 | # Copy the application's requirements.txt and run pip to install all 16 | # dependencies into the virtualenv. 17 | ADD requirements.txt /app/requirements.txt 18 | RUN pip install -r /app/requirements.txt 19 | 20 | # Add the application source code. 21 | ADD . /app 22 | 23 | # Run a WSGI server to serve the application. gunicorn must be declared as 24 | # a dependency in requirements.txt. 25 | CMD gunicorn -b :$PORT main:app 26 | -------------------------------------------------------------------------------- /YOLOv5/utils/google_app_engine/additional_requirements.txt: -------------------------------------------------------------------------------- 1 | # add these requirements in your app on top of the existing ones 2 | pip==18.1 3 | Flask==1.0.2 4 | gunicorn==19.9.0 5 | -------------------------------------------------------------------------------- /YOLOv5/utils/google_app_engine/app.yaml: -------------------------------------------------------------------------------- 1 | runtime: custom 2 | env: flex 3 | 4 | service: yolov5app 5 | 6 | liveness_check: 7 | initial_delay_sec: 600 8 | 9 | manual_scaling: 10 | instances: 1 11 | resources: 12 | cpu: 1 13 | memory_gb: 4 14 | disk_size_gb: 20 -------------------------------------------------------------------------------- /YOLOv5/utils/google_utils.py: -------------------------------------------------------------------------------- 1 | # This file contains google utils: https://cloud.google.com/storage/docs/reference/libraries 2 | # pip install --upgrade google-cloud-storage 3 | # from google.cloud import storage 4 | 5 | import os 6 | import platform 7 | import subprocess 8 | import time 9 | from pathlib import Path 10 | 11 | import torch 12 | 13 | 14 | def gsutil_getsize(url=''): 15 | # gs://bucket/file size https://cloud.google.com/storage/docs/gsutil/commands/du 16 | s = subprocess.check_output('gsutil du %s' % url, shell=True).decode('utf-8') 17 | return eval(s.split(' ')[0]) if len(s) else 0 # bytes 18 | 19 | 20 | def attempt_download(weights): 21 | # Attempt to download pretrained weights if not found locally 22 | weights = weights.strip().replace("'", '') 23 | file = Path(weights).name 24 | 25 | msg = weights + ' missing, try downloading from https://github.com/ultralytics/yolov5/releases/' 26 | models = ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt'] # available models 27 | 28 | if file in models and not os.path.isfile(weights): 29 | # Google Drive 30 | # d = {'yolov5s.pt': '1R5T6rIyy3lLwgFXNms8whc-387H0tMQO', 31 | # 'yolov5m.pt': '1vobuEExpWQVpXExsJ2w-Mbf3HJjWkQJr', 32 | # 'yolov5l.pt': '1hrlqD1Wdei7UT4OgT785BEk1JwnSvNEV', 33 | # 'yolov5x.pt': '1mM8aZJlWTxOg7BZJvNUMrTnA2AbeCVzS'} 34 | # r = gdrive_download(id=d[file], name=weights) if file in d else 1 35 | # if r == 0 and os.path.exists(weights) and os.path.getsize(weights) > 1E6: # check 36 | # return 37 | 38 | try: # GitHub 39 | url = 'https://github.com/ultralytics/yolov5/releases/download/v3.0/' + file 40 | print('Downloading %s to %s...' % (url, weights)) 41 | torch.hub.download_url_to_file(url, weights) 42 | assert os.path.exists(weights) and os.path.getsize(weights) > 1E6 # check 43 | except Exception as e: # GCP 44 | print('Download error: %s' % e) 45 | url = 'https://storage.googleapis.com/ultralytics/yolov5/ckpt/' + file 46 | print('Downloading %s to %s...' % (url, weights)) 47 | r = os.system('curl -L %s -o %s' % (url, weights)) # torch.hub.download_url_to_file(url, weights) 48 | finally: 49 | if not (os.path.exists(weights) and os.path.getsize(weights) > 1E6): # check 50 | os.remove(weights) if os.path.exists(weights) else None # remove partial downloads 51 | print('ERROR: Download failure: %s' % msg) 52 | print('') 53 | return 54 | 55 | 56 | def gdrive_download(id='1n_oKgR81BJtqk75b00eAjdv03qVCQn2f', name='coco128.zip'): 57 | # Downloads a file from Google Drive. from utils.google_utils import *; gdrive_download() 58 | t = time.time() 59 | 60 | print('Downloading https://drive.google.com/uc?export=download&id=%s as %s... ' % (id, name), end='') 61 | os.remove(name) if os.path.exists(name) else None # remove existing 62 | os.remove('cookie') if os.path.exists('cookie') else None 63 | 64 | # Attempt file download 65 | out = "NUL" if platform.system() == "Windows" else "/dev/null" 66 | os.system('curl -c ./cookie -s -L "drive.google.com/uc?export=download&id=%s" > %s ' % (id, out)) 67 | if os.path.exists('cookie'): # large file 68 | s = 'curl -Lb ./cookie "drive.google.com/uc?export=download&confirm=%s&id=%s" -o %s' % (get_token(), id, name) 69 | else: # small file 70 | s = 'curl -s -L -o %s "drive.google.com/uc?export=download&id=%s"' % (name, id) 71 | r = os.system(s) # execute, capture return 72 | os.remove('cookie') if os.path.exists('cookie') else None 73 | 74 | # Error check 75 | if r != 0: 76 | os.remove(name) if os.path.exists(name) else None # remove partial 77 | print('Download error ') # raise Exception('Download error') 78 | return r 79 | 80 | # Unzip if archive 81 | if name.endswith('.zip'): 82 | print('unzipping... ', end='') 83 | os.system('unzip -q %s' % name) # unzip 84 | os.remove(name) # remove zip to free space 85 | 86 | print('Done (%.1fs)' % (time.time() - t)) 87 | return r 88 | 89 | 90 | def get_token(cookie="./cookie"): 91 | with open(cookie) as f: 92 | for line in f: 93 | if "download" in line: 94 | return line.split()[-1] 95 | return "" 96 | 97 | # def upload_blob(bucket_name, source_file_name, destination_blob_name): 98 | # # Uploads a file to a bucket 99 | # # https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-python 100 | # 101 | # storage_client = storage.Client() 102 | # bucket = storage_client.get_bucket(bucket_name) 103 | # blob = bucket.blob(destination_blob_name) 104 | # 105 | # blob.upload_from_filename(source_file_name) 106 | # 107 | # print('File {} uploaded to {}.'.format( 108 | # source_file_name, 109 | # destination_blob_name)) 110 | # 111 | # 112 | # def download_blob(bucket_name, source_blob_name, destination_file_name): 113 | # # Uploads a blob from a bucket 114 | # storage_client = storage.Client() 115 | # bucket = storage_client.get_bucket(bucket_name) 116 | # blob = bucket.blob(source_blob_name) 117 | # 118 | # blob.download_to_filename(destination_file_name) 119 | # 120 | # print('Blob {} downloaded to {}.'.format( 121 | # source_blob_name, 122 | # destination_file_name)) 123 | -------------------------------------------------------------------------------- /YOLOv5/utils/torch_utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import time 4 | from copy import deepcopy 5 | 6 | import math 7 | import torch 8 | import torch.backends.cudnn as cudnn 9 | import torch.nn as nn 10 | import torch.nn.functional as F 11 | import torchvision 12 | 13 | logger = logging.getLogger(__name__) 14 | 15 | 16 | def init_torch_seeds(seed=0): 17 | torch.manual_seed(seed) 18 | 19 | # Speed-reproducibility tradeoff https://pytorch.org/docs/stable/notes/randomness.html 20 | if seed == 0: # slower, more reproducible 21 | cudnn.deterministic = True 22 | cudnn.benchmark = False 23 | else: # faster, less reproducible 24 | cudnn.deterministic = False 25 | cudnn.benchmark = True 26 | 27 | 28 | def select_device(device='', batch_size=None): 29 | # device = 'cpu' or '0' or '0,1,2,3' 30 | cpu_request = device.lower() == 'cpu' 31 | if device and not cpu_request: # if device requested other than 'cpu' 32 | os.environ['CUDA_VISIBLE_DEVICES'] = device # set environment variable 33 | assert torch.cuda.is_available(), 'CUDA unavailable, invalid device %s requested' % device # check availablity 34 | 35 | cuda = False if cpu_request else torch.cuda.is_available() 36 | if cuda: 37 | c = 1024 ** 2 # bytes to MB 38 | ng = torch.cuda.device_count() 39 | if ng > 1 and batch_size: # check that batch_size is compatible with device_count 40 | assert batch_size % ng == 0, 'batch-size %g not multiple of GPU count %g' % (batch_size, ng) 41 | x = [torch.cuda.get_device_properties(i) for i in range(ng)] 42 | s = 'Using CUDA ' 43 | for i in range(0, ng): 44 | if i == 1: 45 | s = ' ' * len(s) 46 | logger.info("%sdevice%g _CudaDeviceProperties(name='%s', total_memory=%dMB)" % 47 | (s, i, x[i].name, x[i].total_memory / c)) 48 | else: 49 | logger.info('Using CPU') 50 | 51 | logger.info('') # skip a line 52 | return torch.device('cuda:0' if cuda else 'cpu') 53 | 54 | 55 | def time_synchronized(): 56 | torch.cuda.synchronize() if torch.cuda.is_available() else None 57 | return time.time() 58 | 59 | 60 | def is_parallel(model): 61 | return type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel) 62 | 63 | 64 | def intersect_dicts(da, db, exclude=()): 65 | # Dictionary intersection of matching keys and shapes, omitting 'exclude' keys, using da values 66 | return {k: v for k, v in da.items() if k in db and not any(x in k for x in exclude) and v.shape == db[k].shape} 67 | 68 | 69 | def initialize_weights(model): 70 | for m in model.modules(): 71 | t = type(m) 72 | if t is nn.Conv2d: 73 | pass # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 74 | elif t is nn.BatchNorm2d: 75 | m.eps = 1e-3 76 | m.momentum = 0.03 77 | elif t in [nn.LeakyReLU, nn.ReLU, nn.ReLU6]: 78 | m.inplace = True 79 | 80 | 81 | def find_modules(model, mclass=nn.Conv2d): 82 | # Finds layer indices matching module class 'mclass' 83 | return [i for i, m in enumerate(model.module_list) if isinstance(m, mclass)] 84 | 85 | 86 | def sparsity(model): 87 | # Return global model sparsity 88 | a, b = 0., 0. 89 | for p in model.parameters(): 90 | a += p.numel() 91 | b += (p == 0).sum() 92 | return b / a 93 | 94 | 95 | def prune(model, amount=0.3): 96 | # Prune model to requested global sparsity 97 | import torch.nn.utils.prune as prune 98 | print('Pruning model... ', end='') 99 | for name, m in model.named_modules(): 100 | if isinstance(m, nn.Conv2d): 101 | prune.l1_unstructured(m, name='weight', amount=amount) # prune 102 | prune.remove(m, 'weight') # make permanent 103 | print(' %.3g global sparsity' % sparsity(model)) 104 | 105 | 106 | def fuse_conv_and_bn(conv, bn): 107 | # Fuse convolution and batchnorm layers https://tehnokv.com/posts/fusing-batchnorm-and-conv/ 108 | 109 | # init 110 | fusedconv = nn.Conv2d(conv.in_channels, 111 | conv.out_channels, 112 | kernel_size=conv.kernel_size, 113 | stride=conv.stride, 114 | padding=conv.padding, 115 | groups=conv.groups, 116 | bias=True).requires_grad_(False).to(conv.weight.device) 117 | 118 | # prepare filters 119 | w_conv = conv.weight.clone().view(conv.out_channels, -1) 120 | w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var))) 121 | fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.size())) 122 | 123 | # prepare spatial bias 124 | b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) if conv.bias is None else conv.bias 125 | b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps)) 126 | fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn) 127 | 128 | return fusedconv 129 | 130 | 131 | def model_info(model, verbose=False): 132 | # Plots a line-by-line description of a PyTorch model 133 | n_p = sum(x.numel() for x in model.parameters()) # number parameters 134 | n_g = sum(x.numel() for x in model.parameters() if x.requires_grad) # number gradients 135 | if verbose: 136 | print('%5s %40s %9s %12s %20s %10s %10s' % ('layer', 'name', 'gradient', 'parameters', 'shape', 'mu', 'sigma')) 137 | for i, (name, p) in enumerate(model.named_parameters()): 138 | name = name.replace('module_list.', '') 139 | print('%5g %40s %9s %12g %20s %10.3g %10.3g' % 140 | (i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std())) 141 | 142 | try: # FLOPS 143 | from thop import profile 144 | flops = profile(deepcopy(model), inputs=(torch.zeros(1, 3, 64, 64),), verbose=False)[0] / 1E9 * 2 145 | fs = ', %.1f GFLOPS' % (flops * 100) # 640x640 FLOPS 146 | except: 147 | fs = '' 148 | 149 | logger.info( 150 | 'Model Summary: %g layers, %g parameters, %g gradients%s' % (len(list(model.parameters())), n_p, n_g, fs)) 151 | 152 | 153 | def load_classifier(name='resnet101', n=2): 154 | # Loads a pretrained model reshaped to n-class output 155 | model = torchvision.models.__dict__[name](pretrained=True) 156 | 157 | # ResNet model properties 158 | # input_size = [3, 224, 224] 159 | # input_space = 'RGB' 160 | # input_range = [0, 1] 161 | # mean = [0.485, 0.456, 0.406] 162 | # std = [0.229, 0.224, 0.225] 163 | 164 | # Reshape output to n classes 165 | filters = model.fc.weight.shape[1] 166 | model.fc.bias = nn.Parameter(torch.zeros(n), requires_grad=True) 167 | model.fc.weight = nn.Parameter(torch.zeros(n, filters), requires_grad=True) 168 | model.fc.out_features = n 169 | return model 170 | 171 | 172 | def scale_img(img, ratio=1.0, same_shape=False): # img(16,3,256,416), r=ratio 173 | # scales img(bs,3,y,x) by ratio 174 | if ratio == 1.0: 175 | return img 176 | else: 177 | h, w = img.shape[2:] 178 | s = (int(h * ratio), int(w * ratio)) # new size 179 | img = F.interpolate(img, size=s, mode='bilinear', align_corners=False) # resize 180 | if not same_shape: # pad/crop img 181 | gs = 32 # (pixels) grid size 182 | h, w = [math.ceil(x * ratio / gs) * gs for x in (h, w)] 183 | return F.pad(img, [0, w - s[1], 0, h - s[0]], value=0.447) # value = imagenet mean 184 | 185 | 186 | def copy_attr(a, b, include=(), exclude=()): 187 | # Copy attributes from b to a, options to only include [...] and to exclude [...] 188 | for k, v in b.__dict__.items(): 189 | if (len(include) and k not in include) or k.startswith('_') or k in exclude: 190 | continue 191 | else: 192 | setattr(a, k, v) 193 | 194 | 195 | class ModelEMA: 196 | """ Model Exponential Moving Average from https://github.com/rwightman/pytorch-image-models 197 | Keep a moving average of everything in the model state_dict (parameters and buffers). 198 | This is intended to allow functionality like 199 | https://www.tensorflow.org/api_docs/python/tf/train/ExponentialMovingAverage 200 | A smoothed version of the weights is necessary for some training schemes to perform well. 201 | This class is sensitive where it is initialized in the sequence of model init, 202 | GPU assignment and distributed training wrappers. 203 | """ 204 | 205 | def __init__(self, model, decay=0.9999, updates=0): 206 | # Create EMA 207 | self.ema = deepcopy(model.module if is_parallel(model) else model).eval() # FP32 EMA 208 | # if next(model.parameters()).device.type != 'cpu': 209 | # self.ema.half() # FP16 EMA 210 | self.updates = updates # number of EMA updates 211 | self.decay = lambda x: decay * (1 - math.exp(-x / 2000)) # decay exponential ramp (to help early epochs) 212 | for p in self.ema.parameters(): 213 | p.requires_grad_(False) 214 | 215 | def update(self, model): 216 | # Update EMA parameters 217 | with torch.no_grad(): 218 | self.updates += 1 219 | d = self.decay(self.updates) 220 | 221 | msd = model.module.state_dict() if is_parallel(model) else model.state_dict() # model state_dict 222 | for k, v in self.ema.state_dict().items(): 223 | if v.dtype.is_floating_point: 224 | v *= d 225 | v += (1. - d) * msd[k].detach() 226 | 227 | def update_attr(self, model, include=(), exclude=('process_group', 'reducer')): 228 | # Update EMA attributes 229 | copy_attr(self.ema, model, include, exclude) 230 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.4 2 | certifi==2020.6.20 3 | click==7.1.2 4 | cycler==0.10.0 5 | Cython==0.29.21 6 | distlib==0.3.1 7 | distro==1.5.0 8 | filelock==3.0.12 9 | Flask==1.1.2 10 | importlib-metadata==2.0.0 11 | itsdangerous==1.1.0 12 | Jinja2==2.11.2 13 | kiwisolver==1.2.0 14 | MarkupSafe==1.1.1 15 | matplotlib==3.3.2 16 | numpy==1.19.2 17 | packaging==20.4 18 | Pillow==7.2.0 19 | pkg-resources==0.0.0 20 | pyparsing==2.4.7 21 | python-dateutil==2.8.1 22 | PyYAML==5.3.1 23 | scikit-build==0.11.1 24 | six==1.15.0 25 | tqdm==4.50.2 26 | virtualenv==20.0.33 27 | Werkzeug==1.0.1 28 | zipp==3.3.0 29 | --------------------------------------------------------------------------------