{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!git clone https://github.com/ultralytics/yolov5\n\n%cd ./yolov5\n!pip install -r requirements.txt\n\nimport torch\nfrom yolov5 import utils\ndisplay = utils.notebook_init()  # checks","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-02-10T21:05:54.687644Z","iopub.execute_input":"2022-02-10T21:05:54.688143Z","iopub.status.idle":"2022-02-10T21:06:09.220084Z","shell.execute_reply.started":"2022-02-10T21:05:54.688047Z","shell.execute_reply":"2022-02-10T21:06:09.219217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -qU wandb","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:09.222853Z","iopub.execute_input":"2022-02-10T21:06:09.2234Z","iopub.status.idle":"2022-02-10T21:06:20.206978Z","shell.execute_reply.started":"2022-02-10T21:06:09.223355Z","shell.execute_reply":"2022-02-10T21:06:20.206066Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nfrom shutil import copyfile\nimport json\nfrom string import Template\nfrom tqdm import tqdm\nfrom sklearn.model_selection import GroupKFold\\","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:20.209915Z","iopub.execute_input":"2022-02-10T21:06:20.210194Z","iopub.status.idle":"2022-02-10T21:06:21.096287Z","shell.execute_reply.started":"2022-02-10T21:06:20.210159Z","shell.execute_reply":"2022-02-10T21:06:21.095577Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import wandb\n\ntry:\n    from kaggle_secrets import UserSecretsClient\n    user_secrets = UserSecretsClient()\n    api_key = user_secrets.get_secret(\"WANDB\")\n    wandb.login(key=api_key)\n    anonymous = None\nexcept:\n    wandb.login(anonymous='must')\n    print('To use your W&B account,\\nGo to Add-ons -> Secrets and provide your W&B access token. Use the Label name as WANDB. \\nGet your W&B access token from here: https://wandb.ai/authorize')\n","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:21.097517Z","iopub.execute_input":"2022-02-10T21:06:21.097801Z","iopub.status.idle":"2022-02-10T21:06:23.502354Z","shell.execute_reply.started":"2022-02-10T21:06:21.097767Z","shell.execute_reply":"2022-02-10T21:06:23.501592Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create the directory to put data converted to Yolo format\n%mkdir datasets\n%mkdir datasets/tf_cots_dataset/\n%mkdir datasets/tf_cots_dataset/images\n%mkdir datasets/tf_cots_dataset/labels\n!touch datasets/tf_cots_dataset/train.txt\n!touch datasets/tf_cots_dataset/valid.txt","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-10T21:06:23.505227Z","iopub.execute_input":"2022-02-10T21:06:23.505464Z","iopub.status.idle":"2022-02-10T21:06:27.492639Z","shell.execute_reply.started":"2022-02-10T21:06:23.505426Z","shell.execute_reply":"2022-02-10T21:06:27.491709Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%ls datasets/tf_cots_dataset/","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:27.494138Z","iopub.execute_input":"2022-02-10T21:06:27.494402Z","iopub.status.idle":"2022-02-10T21:06:28.149345Z","shell.execute_reply.started":"2022-02-10T21:06:27.494364Z","shell.execute_reply":"2022-02-10T21:06:28.148516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainset = pd.read_csv('/kaggle/input/tensorflow-great-barrier-reef/train.csv')\ntrainset.head()","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.15196Z","iopub.execute_input":"2022-02-10T21:06:28.152251Z","iopub.status.idle":"2022-02-10T21:06:28.218066Z","shell.execute_reply.started":"2022-02-10T21:06:28.152211Z","shell.execute_reply":"2022-02-10T21:06:28.217395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_annotation(annot_line, img_size):\n    annotations = json.loads(annot_line.replace(\"'\", '\"'))\n    normalized_annots = []\n    \n    for annotation in annotations:\n        x = round((float(annotation['x']) + (float(annotation['width']) / 2)) / img_size[0], 6)\n        y = round((float(annotation['y']) + (float(annotation['height']) / 2)) / img_size[1], 6)\n        width = round(float(annotation['width']) / img_size[0], 6)\n        height = round(float(annotation['height']) / img_size[1], 6)\n        \n        # we only have one class\n        annot = [0, x, y, width, height]\n        normalized_annots.append(annot)\n    \n    return normalized_annots","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.219236Z","iopub.execute_input":"2022-02-10T21:06:28.219479Z","iopub.status.idle":"2022-02-10T21:06:28.226679Z","shell.execute_reply.started":"2022-02-10T21:06:28.219446Z","shell.execute_reply":"2022-02-10T21:06:28.225926Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labelled_trainset = trainset[trainset['annotations'] != '[]']\nprint(\"number of labeled images/frames:\", len(labelled_trainset))","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.228035Z","iopub.execute_input":"2022-02-10T21:06:28.228512Z","iopub.status.idle":"2022-02-10T21:06:28.245789Z","shell.execute_reply.started":"2022-02-10T21:06:28.228474Z","shell.execute_reply":"2022-02-10T21:06:28.244901Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"negative_imgs = trainset[trainset['annotations'] == '[]'].groupby(['video_id', 'sequence']).sample(15)\nlabelled_trainset = labelled_trainset.append(negative_imgs, ignore_index=True).copy()\nprint(\"number of labeled images/frames + negative images:\", len(labelled_trainset))","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.247369Z","iopub.execute_input":"2022-02-10T21:06:28.247931Z","iopub.status.idle":"2022-02-10T21:06:28.274487Z","shell.execute_reply.started":"2022-02-10T21:06:28.247886Z","shell.execute_reply":"2022-02-10T21:06:28.273795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"negative_imgs.to_csv('../negative_imgs.csv')","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.275652Z","iopub.execute_input":"2022-02-10T21:06:28.27611Z","iopub.status.idle":"2022-02-10T21:06:28.284065Z","shell.execute_reply.started":"2022-02-10T21:06:28.276074Z","shell.execute_reply":"2022-02-10T21:06:28.283253Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMG_SIZE = (1280, 720)\nIMG_DIR = '/kaggle/input/tensorflow-great-barrier-reef/train_images'\nlabelled_trainset['image_path'] = labelled_trainset['image_id'].apply(lambda x: os.path.join(IMG_DIR, 'video_'+ x.split('-')[0] + '/' + x.split('-')[1] + '.jpg'))","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.285713Z","iopub.execute_input":"2022-02-10T21:06:28.286177Z","iopub.status.idle":"2022-02-10T21:06:28.307322Z","shell.execute_reply.started":"2022-02-10T21:06:28.286141Z","shell.execute_reply":"2022-02-10T21:06:28.306674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labelled_trainset","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.308616Z","iopub.execute_input":"2022-02-10T21:06:28.308862Z","iopub.status.idle":"2022-02-10T21:06:28.324171Z","shell.execute_reply.started":"2022-02-10T21:06:28.308829Z","shell.execute_reply":"2022-02-10T21:06:28.323365Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"kf = GroupKFold(n_splits=10)\ntrain_data = labelled_trainset.reset_index(drop=True)\ntrain_data['fold'] = -1\nfor fold, (train_idx, valid_idx) in enumerate(kf.split(train_data, y=train_data['video_id'].tolist(), groups=train_data['sequence'])):\n    train_data.loc[valid_idx, 'fold'] = fold\n    \ntrain_data.head(5)","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.327906Z","iopub.execute_input":"2022-02-10T21:06:28.328151Z","iopub.status.idle":"2022-02-10T21:06:28.351367Z","shell.execute_reply.started":"2022-02-10T21:06:28.328119Z","shell.execute_reply":"2022-02-10T21:06:28.350591Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data['fold'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.352641Z","iopub.execute_input":"2022-02-10T21:06:28.352885Z","iopub.status.idle":"2022-02-10T21:06:28.360771Z","shell.execute_reply.started":"2022-02-10T21:06:28.352853Z","shell.execute_reply":"2022-02-10T21:06:28.359871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_file = open('/kaggle/working/yolov5/datasets/tf_cots_dataset/train.txt', 'w')\nvalid_file = open('/kaggle/working/yolov5/datasets/tf_cots_dataset/valid.txt', 'w')\nvalid_idx  = 9\n\ncnt_neg = 0\n\nfor _, row in tqdm(train_data.iterrows()):\n    copyfile(row['image_path'], f'/kaggle/working/yolov5/datasets/tf_cots_dataset/images/{row[\"image_id\"]}.jpg')\n    # these are negative examples, no bounding box\n    if row[\"annotations\"] == '[]':\n        cnt_neg += 1\n        continue\n        \n    with open(f'/kaggle/working/yolov5/datasets/tf_cots_dataset/labels/{row[\"image_id\"]}.txt', 'w') as f:\n        annotations = load_annotation(row[\"annotations\"], IMG_SIZE)\n        for annotation in annotations:\n            for coord in annotation:\n                f.write(str(coord) + ' ')\n            f.write('\\n')\n    \n    if row['fold'] != valid_idx:\n        train_file.write(f'/kaggle/working/yolov5/datasets/tf_cots_dataset/images/{row[\"image_id\"]}.jpg')\n        train_file.write('\\n')\n    else:\n        valid_file.write(f'/kaggle/working/yolov5/datasets/tf_cots_dataset/images/{row[\"image_id\"]}.jpg')\n        valid_file.write('\\n')\n        \ntrain_file.close()\nvalid_file.close()\n\nprint(\"number of negative example introduced:\", cnt_neg)","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:06:28.36207Z","iopub.execute_input":"2022-02-10T21:06:28.362419Z","iopub.status.idle":"2022-02-10T21:07:29.634213Z","shell.execute_reply.started":"2022-02-10T21:06:28.362382Z","shell.execute_reply":"2022-02-10T21:07:29.633462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(os.listdir('/kaggle/working/yolov5/datasets/tf_cots_dataset/images')))\nprint(len(os.listdir('/kaggle/working/yolov5/datasets/tf_cots_dataset/labels')))","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:29.635752Z","iopub.execute_input":"2022-02-10T21:07:29.636233Z","iopub.status.idle":"2022-02-10T21:07:29.648928Z","shell.execute_reply.started":"2022-02-10T21:07:29.636195Z","shell.execute_reply":"2022-02-10T21:07:29.648253Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with open('/kaggle/working/yolov5/datasets/tf_cots_dataset/train.txt', 'r') as f:\n    results = f.readlines()\nprint(\"number of train images:\", len(results))","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:29.650183Z","iopub.execute_input":"2022-02-10T21:07:29.650437Z","iopub.status.idle":"2022-02-10T21:07:30.871444Z","shell.execute_reply.started":"2022-02-10T21:07:29.650403Z","shell.execute_reply":"2022-02-10T21:07:30.870581Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with open('/kaggle/working/yolov5/datasets/tf_cots_dataset/valid.txt', 'r') as f:\n    results = f.readlines()\nprint(\"number of validation images:\", len(results))","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:30.874686Z","iopub.execute_input":"2022-02-10T21:07:30.874922Z","iopub.status.idle":"2022-02-10T21:07:30.88171Z","shell.execute_reply.started":"2022-02-10T21:07:30.874894Z","shell.execute_reply":"2022-02-10T21:07:30.880798Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile /kaggle/working/yolov5/data/tf_cots_dataset.yaml\n# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]\npath: /kaggle/working/yolov5/datasets/tf_cots_dataset/  # dataset root dir\ntrain: train.txt  # train images (relative to 'path') 128 images\nval: valid.txt  # val images (relative to 'path') 128 images\ntest:  # test images (optional)\n\n# Classes\nnc: 1  # number of classes\nnames: ['starfish']  # class names","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:30.883299Z","iopub.execute_input":"2022-02-10T21:07:30.883801Z","iopub.status.idle":"2022-02-10T21:07:30.891418Z","shell.execute_reply.started":"2022-02-10T21:07:30.883759Z","shell.execute_reply":"2022-02-10T21:07:30.890588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile /kaggle/working/yolov5/data/hyps/hyp.scratch.yaml\n# YOLOv5 🚀 by Ultralytics, GPL-3.0 license\n# Hyperparameters for COCO training from scratch\n# python train.py --batch 40 --cfg yolov5m.yaml --weights '' --data coco.yaml --img 640 --epochs 300\n# See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials\n\nlr0: 0.001  # initial learning rate (SGD=1E-2, Adam=1E-3)\nlrf: 0.1  # final OneCycleLR learning rate (lr0 * lrf)\nmomentum: 0.937  # SGD momentum/Adam beta1\nweight_decay: 0.0005  # optimizer weight decay 5e-4\nwarmup_epochs: 2.0  # warmup epochs (fractions ok)\nwarmup_momentum: 0.8  # warmup initial momentum\nwarmup_bias_lr: 0.1  # warmup initial bias lr\nbox: 0.05  # box loss gain\ncls: 0.5  # cls loss gain\ncls_pw: 1.0  # cls BCELoss positive_weight\nobj: 1.0  # obj loss gain (scale with pixels)\nobj_pw: 2.0  # obj BCELoss positive_weight\niou_t: 0.20  # IoU training threshold\nanchor_t: 4.0  # anchor-multiple threshold\n# anchors: 3  # anchors per output layer (0 to ignore)\nfl_gamma: 0.0  # focal loss gamma (efficientDet default gamma=1.5)\nhsv_h: 0.015  # image HSV-Hue augmentation (fraction)\nhsv_s: 0.2  # image HSV-Saturation augmentation (fraction)\nhsv_v: 0.7  # image HSV-Value augmentation (fraction)\ndegrees: 0.0  # image rotation (+/- deg)\ntranslate: 0.1  # image translation (+/- fraction)\nscale: 0.5  # image scale (+/- gain)\nshear: 0.0  # image shear (+/- deg)\nperspective: 0.0  # image perspective (+/- fraction), range 0-0.001\nflipud: 0.5  # image flip up-down (probability)\nfliplr: 0.5  # image flip left-right (probability)\nmosaic: 0.5  # image mosaic (probability)\nmixup: 0.3  # image mixup (probability)\ncopy_paste: 0.0  # segment copy-paste (probability)","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:30.893026Z","iopub.execute_input":"2022-02-10T21:07:30.893558Z","iopub.status.idle":"2022-02-10T21:07:30.902945Z","shell.execute_reply.started":"2022-02-10T21:07:30.893488Z","shell.execute_reply":"2022-02-10T21:07:30.90196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile /kaggle/working/yolov5/utils/augmentations.py\n# YOLOv5 🚀 by Ultralytics, GPL-3.0 license\n\"\"\"\nImage augmentation functions\n\"\"\"\n\nimport math\nimport random\n\nimport cv2\nimport numpy as np\n\nfrom utils.general import LOGGER, check_version, colorstr, resample_segments, segment2box\nfrom utils.metrics import bbox_ioa\n\n\nclass Albumentations:\n    # YOLOv5 Albumentations class (optional, only used if package is installed)\n    def __init__(self):\n        self.transform = None\n        try:\n            import albumentations as A\n            check_version(A.__version__, '1.0.3', hard=True)  # version requirement\n\n            self.transform = A.Compose([\n                A.Blur(p=0.0),\n                A.MedianBlur(p=0.1, blur_limit=(30, 30)),\n                A.ToGray(p=0.0),\n                A.CLAHE(p=0.3),\n                A.RandomBrightnessContrast(p=0.2),\n                A.RandomGamma(p=0.0),\n                A.GaussNoise(p=0.2, var_limit=(50.0, 200.0)),\n                A.ImageCompression(quality_lower=75, p=0.0)],\n                bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels']))\n\n            LOGGER.info(colorstr('albumentations: ') + ', '.join(f'{x}' for x in self.transform.transforms if x.p))\n        except ImportError:  # package not installed, skip\n            pass\n        except Exception as e:\n            LOGGER.info(colorstr('albumentations: ') + f'{e}')\n\n    def __call__(self, im, labels, p=1.0):\n        if self.transform and random.random() < p:\n            new = self.transform(image=im, bboxes=labels[:, 1:], class_labels=labels[:, 0])  # transformed\n            im, labels = new['image'], np.array([[c, *b] for c, b in zip(new['class_labels'], new['bboxes'])])\n        return im, labels\n\n\ndef augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):\n    # HSV color-space augmentation\n    if hgain or sgain or vgain:\n        r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1  # random gains\n        hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV))\n        dtype = im.dtype  # uint8\n\n        x = np.arange(0, 256, dtype=r.dtype)\n        lut_hue = ((x * r[0]) % 180).astype(dtype)\n        lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)\n        lut_val = np.clip(x * r[2], 0, 255).astype(dtype)\n\n        im_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))\n        cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=im)  # no return needed\n\n\ndef hist_equalize(im, clahe=True, bgr=False):\n    # Equalize histogram on BGR image 'im' with im.shape(n,m,3) and range 0-255\n    yuv = cv2.cvtColor(im, cv2.COLOR_BGR2YUV if bgr else cv2.COLOR_RGB2YUV)\n    if clahe:\n        c = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n        yuv[:, :, 0] = c.apply(yuv[:, :, 0])\n    else:\n        yuv[:, :, 0] = cv2.equalizeHist(yuv[:, :, 0])  # equalize Y channel histogram\n    return cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR if bgr else cv2.COLOR_YUV2RGB)  # convert YUV image to RGB\n\n\ndef replicate(im, labels):\n    # Replicate labels\n    h, w = im.shape[:2]\n    boxes = labels[:, 1:].astype(int)\n    x1, y1, x2, y2 = boxes.T\n    s = ((x2 - x1) + (y2 - y1)) / 2  # side length (pixels)\n    for i in s.argsort()[:round(s.size * 0.5)]:  # smallest indices\n        x1b, y1b, x2b, y2b = boxes[i]\n        bh, bw = y2b - y1b, x2b - x1b\n        yc, xc = int(random.uniform(0, h - bh)), int(random.uniform(0, w - bw))  # offset x, y\n        x1a, y1a, x2a, y2a = [xc, yc, xc + bw, yc + bh]\n        im[y1a:y2a, x1a:x2a] = im[y1b:y2b, x1b:x2b]  # im4[ymin:ymax, xmin:xmax]\n        labels = np.append(labels, [[labels[i, 0], x1a, y1a, x2a, y2a]], axis=0)\n\n    return im, labels\n\n\ndef letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):\n    # Resize and pad image while meeting stride-multiple constraints\n    shape = im.shape[:2]  # current shape [height, width]\n    if isinstance(new_shape, int):\n        new_shape = (new_shape, new_shape)\n\n    # Scale ratio (new / old)\n    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])\n    if not scaleup:  # only scale down, do not scale up (for better val mAP)\n        r = min(r, 1.0)\n\n    # Compute padding\n    ratio = r, r  # width, height ratios\n    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))\n    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding\n    if auto:  # minimum rectangle\n        dw, dh = np.mod(dw, stride), np.mod(dh, stride)  # wh padding\n    elif scaleFill:  # stretch\n        dw, dh = 0.0, 0.0\n        new_unpad = (new_shape[1], new_shape[0])\n        ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratios\n\n    dw /= 2  # divide padding into 2 sides\n    dh /= 2\n\n    if shape[::-1] != new_unpad:  # resize\n        im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)\n    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))\n    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))\n    im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border\n    return im, ratio, (dw, dh)\n\n\ndef random_perspective(im, targets=(), segments=(), degrees=10, translate=.1, scale=.1, shear=10, perspective=0.0,\n                       border=(0, 0)):\n    # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(0.1, 0.1), scale=(0.9, 1.1), shear=(-10, 10))\n    # targets = [cls, xyxy]\n\n    height = im.shape[0] + border[0] * 2  # shape(h,w,c)\n    width = im.shape[1] + border[1] * 2\n\n    # Center\n    C = np.eye(3)\n    C[0, 2] = -im.shape[1] / 2  # x translation (pixels)\n    C[1, 2] = -im.shape[0] / 2  # y translation (pixels)\n\n    # Perspective\n    P = np.eye(3)\n    P[2, 0] = random.uniform(-perspective, perspective)  # x perspective (about y)\n    P[2, 1] = random.uniform(-perspective, perspective)  # y perspective (about x)\n\n    # Rotation and Scale\n    R = np.eye(3)\n    a = random.uniform(-degrees, degrees)\n    # a += random.choice([-180, -90, 0, 90])  # add 90deg rotations to small rotations\n    s = random.uniform(1 - scale, 1 + scale)\n    # s = 2 ** random.uniform(-scale, scale)\n    R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s)\n\n    # Shear\n    S = np.eye(3)\n    S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # x shear (deg)\n    S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # y shear (deg)\n\n    # Translation\n    T = np.eye(3)\n    T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * width  # x translation (pixels)\n    T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * height  # y translation (pixels)\n\n    # Combined rotation matrix\n    M = T @ S @ R @ P @ C  # order of operations (right to left) is IMPORTANT\n    if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any():  # image changed\n        if perspective:\n            im = cv2.warpPerspective(im, M, dsize=(width, height), borderValue=(114, 114, 114))\n        else:  # affine\n            im = cv2.warpAffine(im, M[:2], dsize=(width, height), borderValue=(114, 114, 114))\n\n    # Visualize\n    # import matplotlib.pyplot as plt\n    # ax = plt.subplots(1, 2, figsize=(12, 6))[1].ravel()\n    # ax[0].imshow(im[:, :, ::-1])  # base\n    # ax[1].imshow(im2[:, :, ::-1])  # warped\n\n    # Transform label coordinates\n    n = len(targets)\n    if n:\n        use_segments = any(x.any() for x in segments)\n        new = np.zeros((n, 4))\n        if use_segments:  # warp segments\n            segments = resample_segments(segments)  # upsample\n            for i, segment in enumerate(segments):\n                xy = np.ones((len(segment), 3))\n                xy[:, :2] = segment\n                xy = xy @ M.T  # transform\n                xy = xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]  # perspective rescale or affine\n\n                # clip\n                new[i] = segment2box(xy, width, height)\n\n        else:  # warp boxes\n            xy = np.ones((n * 4, 3))\n            xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2)  # x1y1, x2y2, x1y2, x2y1\n            xy = xy @ M.T  # transform\n            xy = (xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]).reshape(n, 8)  # perspective rescale or affine\n\n            # create new boxes\n            x = xy[:, [0, 2, 4, 6]]\n            y = xy[:, [1, 3, 5, 7]]\n            new = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T\n\n            # clip\n            new[:, [0, 2]] = new[:, [0, 2]].clip(0, width)\n            new[:, [1, 3]] = new[:, [1, 3]].clip(0, height)\n\n        # filter candidates\n        i = box_candidates(box1=targets[:, 1:5].T * s, box2=new.T, area_thr=0.01 if use_segments else 0.10)\n        targets = targets[i]\n        targets[:, 1:5] = new[i]\n\n    return im, targets\n\n\ndef copy_paste(im, labels, segments, p=0.5):\n    # Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy)\n    n = len(segments)\n    if p and n:\n        h, w, c = im.shape  # height, width, channels\n        im_new = np.zeros(im.shape, np.uint8)\n        for j in random.sample(range(n), k=round(p * n)):\n            l, s = labels[j], segments[j]\n            box = w - l[3], l[2], w - l[1], l[4]\n            ioa = bbox_ioa(box, labels[:, 1:5])  # intersection over area\n            if (ioa < 0.30).all():  # allow 30% obscuration of existing labels\n                labels = np.concatenate((labels, [[l[0], *box]]), 0)\n                segments.append(np.concatenate((w - s[:, 0:1], s[:, 1:2]), 1))\n                cv2.drawContours(im_new, [segments[j].astype(np.int32)], -1, (255, 255, 255), cv2.FILLED)\n\n        result = cv2.bitwise_and(src1=im, src2=im_new)\n        result = cv2.flip(result, 1)  # augment segments (flip left-right)\n        i = result > 0  # pixels to replace\n        # i[:, :] = result.max(2).reshape(h, w, 1)  # act over ch\n        im[i] = result[i]  # cv2.imwrite('debug.jpg', im)  # debug\n\n    return im, labels, segments\n\n\ndef cutout(im, labels, p=0.5):\n    # Applies image cutout augmentation https://arxiv.org/abs/1708.04552\n    if random.random() < p:\n        h, w = im.shape[:2]\n        scales = [0.5] * 1 + [0.25] * 2 + [0.125] * 4 + [0.0625] * 8 + [0.03125] * 16  # image size fraction\n        for s in scales:\n            mask_h = random.randint(1, int(h * s))  # create random masks\n            mask_w = random.randint(1, int(w * s))\n\n            # box\n            xmin = max(0, random.randint(0, w) - mask_w // 2)\n            ymin = max(0, random.randint(0, h) - mask_h // 2)\n            xmax = min(w, xmin + mask_w)\n            ymax = min(h, ymin + mask_h)\n\n            # apply random color mask\n            im[ymin:ymax, xmin:xmax] = [random.randint(64, 191) for _ in range(3)]\n\n            # return unobscured labels\n            if len(labels) and s > 0.03:\n                box = np.array([xmin, ymin, xmax, ymax], dtype=np.float32)\n                ioa = bbox_ioa(box, labels[:, 1:5])  # intersection over area\n                labels = labels[ioa < 0.60]  # remove >60% obscured labels\n\n    return labels\n\n\ndef mixup(im, labels, im2, labels2):\n    # Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf\n    r = np.random.beta(32.0, 32.0)  # mixup ratio, alpha=beta=32.0\n    im = (im * r + im2 * (1 - r)).astype(np.uint8)\n    labels = np.concatenate((labels, labels2), 0)\n    return im, labels\n\n\ndef box_candidates(box1, box2, wh_thr=2, ar_thr=100, area_thr=0.1, eps=1e-16):  # box1(4,n), box2(4,n)\n    # Compute candidate boxes: box1 before augment, box2 after augment, wh_thr (pixels), aspect_ratio_thr, area_ratio\n    w1, h1 = box1[2] - box1[0], box1[3] - box1[1]\n    w2, h2 = box2[2] - box2[0], box2[3] - box2[1]\n    ar = np.maximum(w2 / (h2 + eps), h2 / (w2 + eps))  # aspect ratio\n    return (w2 > wh_thr) & (h2 > wh_thr) & (w2 * h2 / (w1 * h1 + eps) > area_thr) & (ar < ar_thr)  # candidates\n","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:30.904789Z","iopub.execute_input":"2022-02-10T21:07:30.905231Z","iopub.status.idle":"2022-02-10T21:07:30.920769Z","shell.execute_reply.started":"2022-02-10T21:07:30.905192Z","shell.execute_reply":"2022-02-10T21:07:30.91986Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile /kaggle/working/yolov5/utils/metrics.py\n# YOLOv5 🚀 by Ultralytics, GPL-3.0 license\n\"\"\"\nModel validation metrics\n\"\"\"\n\nimport math\nimport warnings\nfrom pathlib import Path\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport torch\n\n\ndef fitness(x):\n    # Model fitness as a weighted combination of metrics\n    w = [0.0, 0.0, 0.0, 0.0, 1.0]  # weights for [P, R, mAP@0.5, mAP@0.5:0.95, F2@0.3:0.8]\n    return (x[:, :5] * w).sum(1)\n\n\ndef ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names=(), eps=1e-16):\n    \"\"\" Compute the average precision, given the recall and precision curves.\n    Source: https://github.com/rafaelpadilla/Object-Detection-Metrics.\n    # Arguments\n        tp:  True positives (nparray, nx1 or nx10).\n        conf:  Objectness value from 0-1 (nparray).\n        pred_cls:  Predicted object classes (nparray).\n        target_cls:  True object classes (nparray).\n        plot:  Plot precision-recall curve at mAP@0.5\n        save_dir:  Plot save directory\n    # Returns\n        The average precision as computed in py-faster-rcnn.\n    \"\"\"\n\n    # Sort by objectness\n    i = np.argsort(-conf)\n    tp, conf, pred_cls = tp[i], conf[i], pred_cls[i]\n\n    # Find unique classes\n    unique_classes, nt = np.unique(target_cls, return_counts=True)\n    nc = unique_classes.shape[0]  # number of classes, number of detections\n\n    # Create Precision-Recall curve and compute AP for each class\n    px, py = np.linspace(0, 1, 1000), []  # for plotting\n    ap, p, r = np.zeros((nc, tp.shape[1])), np.zeros((nc, 1000)), np.zeros((nc, 1000))\n    for ci, c in enumerate(unique_classes):\n        i = pred_cls == c\n        n_l = nt[ci]  # number of labels\n        n_p = i.sum()  # number of predictions\n\n        if n_p == 0 or n_l == 0:\n            continue\n        else:\n            # Accumulate FPs and TPs\n            fpc = (1 - tp[i]).cumsum(0)\n            tpc = tp[i].cumsum(0)\n\n            # Recall\n            recall = tpc / (n_l + eps)  # recall curve\n            r[ci] = np.interp(-px, -conf[i], recall[:, 0], left=0)  # negative x, xp because xp decreases\n\n            # Precision\n            precision = tpc / (tpc + fpc)  # precision curve\n            p[ci] = np.interp(-px, -conf[i], precision[:, 0], left=1)  # p at pr_score\n\n            # AP from recall-precision curve\n            for j in range(tp.shape[1]):\n                ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j])\n                if plot and j == 0:\n                    py.append(np.interp(px, mrec, mpre))  # precision at mAP@0.5\n\n    # Compute F1 (harmonic mean of precision and recall)\n    # f1 = 2 * p * r / (p + r + eps)\n    f2 = 5 * p * r / (4 * p + r + 1e-16)\n    names = [v for k, v in names.items() if k in unique_classes]  # list: only classes that have data\n    names = {i: v for i, v in enumerate(names)}  # to dict\n    if plot:\n        plot_pr_curve(px, py, ap, Path(save_dir) / 'PR_curve.png', names)\n        # plot_mc_curve(px, f1, Path(save_dir) / 'F1_curve.png', names, ylabel='F1')\n        plot_mc_curve(px, f2, Path(save_dir) / 'F2_curve.png', names, ylabel='F2')\n        plot_mc_curve(px, p, Path(save_dir) / 'P_curve.png', names, ylabel='Precision')\n        plot_mc_curve(px, r, Path(save_dir) / 'R_curve.png', names, ylabel='Recall')\n\n    # i = f1.mean(0).argmax()  # max F1 index\n    i = f2.mean(0).argmax() \n    # p, r, f1 = p[:, i], r[:, i], f1[:, i]\n    p, r, f2 = p[:, i], r[:, i], f2[:, i]\n    tp = (r * nt).round()  # true positives\n    fp = (tp / (p + eps) - tp).round()  # false positives\n    # return tp, fp, p, r, f1, ap, unique_classes.astype('int32')\n    return tp, fp, p, r, f2, ap, unique_classes.astype('int32')\n\n\ndef compute_ap(recall, precision):\n    \"\"\" Compute the average precision, given the recall and precision curves\n    # Arguments\n        recall:    The recall curve (list)\n        precision: The precision curve (list)\n    # Returns\n        Average precision, precision curve, recall curve\n    \"\"\"\n\n    # Append sentinel values to beginning and end\n    mrec = np.concatenate(([0.0], recall, [1.0]))\n    mpre = np.concatenate(([1.0], precision, [0.0]))\n\n    # Compute the precision envelope\n    mpre = np.flip(np.maximum.accumulate(np.flip(mpre)))\n\n    # Integrate area under curve\n    method = 'interp'  # methods: 'continuous', 'interp'\n    if method == 'interp':\n        x = np.linspace(0, 1, 101)  # 101-point interp (COCO)\n        ap = np.trapz(np.interp(x, mrec, mpre), x)  # integrate\n    else:  # 'continuous'\n        i = np.where(mrec[1:] != mrec[:-1])[0]  # points where x axis (recall) changes\n        ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])  # area under curve\n\n    return ap, mpre, mrec\n\n\nclass ConfusionMatrix:\n    # Updated version of https://github.com/kaanakan/object_detection_confusion_matrix\n    def __init__(self, nc, conf=0.25, iou_thres=0.45):\n        self.matrix = np.zeros((nc + 1, nc + 1))\n        self.nc = nc  # number of classes\n        self.conf = conf\n        self.iou_thres = iou_thres\n\n    def process_batch(self, detections, labels):\n        \"\"\"\n        Return intersection-over-union (Jaccard index) of boxes.\n        Both sets of boxes are expected to be in (x1, y1, x2, y2) format.\n        Arguments:\n            detections (Array[N, 6]), x1, y1, x2, y2, conf, class\n            labels (Array[M, 5]), class, x1, y1, x2, y2\n        Returns:\n            None, updates confusion matrix accordingly\n        \"\"\"\n        detections = detections[detections[:, 4] > self.conf]\n        gt_classes = labels[:, 0].int()\n        detection_classes = detections[:, 5].int()\n        iou = box_iou(labels[:, 1:], detections[:, :4])\n\n        x = torch.where(iou > self.iou_thres)\n        if x[0].shape[0]:\n            matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy()\n            if x[0].shape[0] > 1:\n                matches = matches[matches[:, 2].argsort()[::-1]]\n                matches = matches[np.unique(matches[:, 1], return_index=True)[1]]\n                matches = matches[matches[:, 2].argsort()[::-1]]\n                matches = matches[np.unique(matches[:, 0], return_index=True)[1]]\n        else:\n            matches = np.zeros((0, 3))\n\n        n = matches.shape[0] > 0\n        m0, m1, _ = matches.transpose().astype(np.int16)\n        for i, gc in enumerate(gt_classes):\n            j = m0 == i\n            if n and sum(j) == 1:\n                self.matrix[detection_classes[m1[j]], gc] += 1  # correct\n            else:\n                self.matrix[self.nc, gc] += 1  # background FP\n\n        if n:\n            for i, dc in enumerate(detection_classes):\n                if not any(m1 == i):\n                    self.matrix[dc, self.nc] += 1  # background FN\n\n    def matrix(self):\n        return self.matrix\n\n    def tp_fp(self):\n        tp = self.matrix.diagonal()  # true positives\n        fp = self.matrix.sum(1) - tp  # false positives\n        # fn = self.matrix.sum(0) - tp  # false negatives (missed detections)\n        return tp[:-1], fp[:-1]  # remove background class\n\n    def plot(self, normalize=True, save_dir='', names=()):\n        try:\n            import seaborn as sn\n\n            array = self.matrix / ((self.matrix.sum(0).reshape(1, -1) + 1E-6) if normalize else 1)  # normalize columns\n            array[array < 0.005] = np.nan  # don't annotate (would appear as 0.00)\n\n            fig = plt.figure(figsize=(12, 9), tight_layout=True)\n            sn.set(font_scale=1.0 if self.nc < 50 else 0.8)  # for label size\n            labels = (0 < len(names) < 99) and len(names) == self.nc  # apply names to ticklabels\n            with warnings.catch_warnings():\n                warnings.simplefilter('ignore')  # suppress empty matrix RuntimeWarning: All-NaN slice encountered\n                sn.heatmap(array, annot=self.nc < 30, annot_kws={\"size\": 8}, cmap='Blues', fmt='.2f', square=True,\n                           xticklabels=names + ['background FP'] if labels else \"auto\",\n                           yticklabels=names + ['background FN'] if labels else \"auto\").set_facecolor((1, 1, 1))\n            fig.axes[0].set_xlabel('True')\n            fig.axes[0].set_ylabel('Predicted')\n            fig.savefig(Path(save_dir) / 'confusion_matrix.png', dpi=250)\n            plt.close()\n        except Exception as e:\n            print(f'WARNING: ConfusionMatrix plot failure: {e}')\n\n    def print(self):\n        for i in range(self.nc + 1):\n            print(' '.join(map(str, self.matrix[i])))\n\n\ndef bbox_iou(box1, box2, x1y1x2y2=True, GIoU=False, DIoU=False, CIoU=False, eps=1e-7):\n    # Returns the IoU of box1 to box2. box1 is 4, box2 is nx4\n    box2 = box2.T\n\n    # Get the coordinates of bounding boxes\n    if x1y1x2y2:  # x1, y1, x2, y2 = box1\n        b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3]\n        b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3]\n    else:  # transform from xywh to xyxy\n        b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2\n        b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2\n        b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2\n        b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2\n\n    # Intersection area\n    inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \\\n            (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)\n\n    # Union Area\n    w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps\n    w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps\n    union = w1 * h1 + w2 * h2 - inter + eps\n\n    iou = inter / union\n    if CIoU or DIoU or GIoU:\n        cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)  # convex (smallest enclosing box) width\n        ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)  # convex height\n        if CIoU or DIoU:  # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1\n            c2 = cw ** 2 + ch ** 2 + eps  # convex diagonal squared\n            rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 +\n                    (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4  # center distance squared\n            if CIoU:  # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47\n                v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)\n                with torch.no_grad():\n                    alpha = v / (v - iou + (1 + eps))\n                return iou - (rho2 / c2 + v * alpha)  # CIoU\n            return iou - rho2 / c2  # DIoU\n        c_area = cw * ch + eps  # convex area\n        return iou - (c_area - union) / c_area  # GIoU https://arxiv.org/pdf/1902.09630.pdf\n    return iou  # IoU\n\n\ndef box_iou(box1, box2):\n    # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py\n    \"\"\"\n    Return intersection-over-union (Jaccard index) of boxes.\n    Both sets of boxes are expected to be in (x1, y1, x2, y2) format.\n    Arguments:\n        box1 (Tensor[N, 4])\n        box2 (Tensor[M, 4])\n    Returns:\n        iou (Tensor[N, M]): the NxM matrix containing the pairwise\n            IoU values for every element in boxes1 and boxes2\n    \"\"\"\n\n    def box_area(box):\n        # box = 4xn\n        return (box[2] - box[0]) * (box[3] - box[1])\n\n    area1 = box_area(box1.T)\n    area2 = box_area(box2.T)\n\n    # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)\n    inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2)\n    return inter / (area1[:, None] + area2 - inter)  # iou = inter / (area1 + area2 - inter)\n\n\ndef bbox_ioa(box1, box2, eps=1E-7):\n    \"\"\" Returns the intersection over box2 area given box1, box2. Boxes are x1y1x2y2\n    box1:       np.array of shape(4)\n    box2:       np.array of shape(nx4)\n    returns:    np.array of shape(n)\n    \"\"\"\n\n    box2 = box2.transpose()\n\n    # Get the coordinates of bounding boxes\n    b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3]\n    b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3]\n\n    # Intersection area\n    inter_area = (np.minimum(b1_x2, b2_x2) - np.maximum(b1_x1, b2_x1)).clip(0) * \\\n                 (np.minimum(b1_y2, b2_y2) - np.maximum(b1_y1, b2_y1)).clip(0)\n\n    # box2 area\n    box2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1) + eps\n\n    # Intersection over box2 area\n    return inter_area / box2_area\n\n\ndef wh_iou(wh1, wh2):\n    # Returns the nxm IoU matrix. wh1 is nx2, wh2 is mx2\n    wh1 = wh1[:, None]  # [N,1,2]\n    wh2 = wh2[None]  # [1,M,2]\n    inter = torch.min(wh1, wh2).prod(2)  # [N,M]\n    return inter / (wh1.prod(2) + wh2.prod(2) - inter)  # iou = inter / (area1 + area2 - inter)\n\n\n# Plots ----------------------------------------------------------------------------------------------------------------\n\ndef plot_pr_curve(px, py, ap, save_dir='pr_curve.png', names=()):\n    # Precision-recall curve\n    fig, ax = plt.subplots(1, 1, figsize=(9, 6), tight_layout=True)\n    py = np.stack(py, axis=1)\n\n    if 0 < len(names) < 21:  # display per-class legend if < 21 classes\n        for i, y in enumerate(py.T):\n            ax.plot(px, y, linewidth=1, label=f'{names[i]} {ap[i, 0]:.3f}')  # plot(recall, precision)\n    else:\n        ax.plot(px, py, linewidth=1, color='grey')  # plot(recall, precision)\n\n    ax.plot(px, py.mean(1), linewidth=3, color='blue', label='all classes %.3f mAP@0.5' % ap[:, 0].mean())\n    ax.set_xlabel('Recall')\n    ax.set_ylabel('Precision')\n    ax.set_xlim(0, 1)\n    ax.set_ylim(0, 1)\n    plt.legend(bbox_to_anchor=(1.04, 1), loc=\"upper left\")\n    fig.savefig(Path(save_dir), dpi=250)\n    plt.close()\n\n\ndef plot_mc_curve(px, py, save_dir='mc_curve.png', names=(), xlabel='Confidence', ylabel='Metric'):\n    # Metric-confidence curve\n    fig, ax = plt.subplots(1, 1, figsize=(9, 6), tight_layout=True)\n\n    if 0 < len(names) < 21:  # display per-class legend if < 21 classes\n        for i, y in enumerate(py):\n            ax.plot(px, y, linewidth=1, label=f'{names[i]}')  # plot(confidence, metric)\n    else:\n        ax.plot(px, py.T, linewidth=1, color='grey')  # plot(confidence, metric)\n\n    y = py.mean(0)\n    ax.plot(px, y, linewidth=3, color='blue', label=f'all classes {y.max():.2f} at {px[y.argmax()]:.3f}')\n    ax.set_xlabel(xlabel)\n    ax.set_ylabel(ylabel)\n    ax.set_xlim(0, 1)\n    ax.set_ylim(0, 1)\n    plt.legend(bbox_to_anchor=(1.04, 1), loc=\"upper left\")\n    fig.savefig(Path(save_dir), dpi=250)\n    plt.close()","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:30.923334Z","iopub.execute_input":"2022-02-10T21:07:30.923966Z","iopub.status.idle":"2022-02-10T21:07:30.940153Z","shell.execute_reply.started":"2022-02-10T21:07:30.923883Z","shell.execute_reply":"2022-02-10T21:07:30.939102Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile /kaggle/working/yolov5/val.py\n# YOLOv5 🚀 by Ultralytics, GPL-3.0 license\n\"\"\"\nValidate a trained YOLOv5 model accuracy on a custom dataset\nUsage:\n    $ python path/to/val.py --weights yolov5s.pt --data coco128.yaml --img 640\nUsage - formats:\n    $ python path/to/val.py --weights yolov5s.pt                 # PyTorch\n                                      yolov5s.torchscript        # TorchScript\n                                      yolov5s.onnx               # ONNX Runtime or OpenCV DNN with --dnn\n                                      yolov5s.xml                # OpenVINO\n                                      yolov5s.engine             # TensorRT\n                                      yolov5s.mlmodel            # CoreML (MacOS-only)\n                                      yolov5s_saved_model        # TensorFlow SavedModel\n                                      yolov5s.pb                 # TensorFlow GraphDef\n                                      yolov5s.tflite             # TensorFlow Lite\n                                      yolov5s_edgetpu.tflite     # TensorFlow Edge TPU\n\"\"\"\n\nimport argparse\nimport json\nimport os\nimport sys\nfrom pathlib import Path\nfrom threading import Thread\n\nimport numpy as np\nimport torch\nfrom tqdm import tqdm\n\nFILE = Path(__file__).resolve()\nROOT = FILE.parents[0]  # YOLOv5 root directory\nif str(ROOT) not in sys.path:\n    sys.path.append(str(ROOT))  # add ROOT to PATH\nROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative\n\nfrom models.common import DetectMultiBackend\nfrom utils.callbacks import Callbacks\nfrom utils.datasets import create_dataloader\nfrom utils.general import (LOGGER, box_iou, check_dataset, check_img_size, check_requirements, check_yaml,\n                           coco80_to_coco91_class, colorstr, increment_path, non_max_suppression, print_args,\n                           scale_coords, xywh2xyxy, xyxy2xywh)\nfrom utils.metrics import ConfusionMatrix, ap_per_class\nfrom utils.plots import output_to_target, plot_images, plot_val_study\nfrom utils.torch_utils import select_device, time_sync\n\n\ndef save_one_txt(predn, save_conf, shape, file):\n    # Save one txt result\n    gn = torch.tensor(shape)[[1, 0, 1, 0]]  # normalization gain whwh\n    for *xyxy, conf, cls in predn.tolist():\n        xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh\n        line = (cls, *xywh, conf) if save_conf else (cls, *xywh)  # label format\n        with open(file, 'a') as f:\n            f.write(('%g ' * len(line)).rstrip() % line + '\\n')\n\ndef save_one_json(predn, jdict, path, class_map):\n    # Save one JSON result {\"image_id\": 42, \"category_id\": 18, \"bbox\": [258.15, 41.29, 348.26, 243.78], \"score\": 0.236}\n    image_id = int(path.stem) if path.stem.isnumeric() else path.stem\n    box = xyxy2xywh(predn[:, :4])  # xywh\n    box[:, :2] -= box[:, 2:] / 2  # xy center to top-left corner\n    for p, b in zip(predn.tolist(), box.tolist()):\n        jdict.append({'image_id': image_id,\n                      'category_id': class_map[int(p[5])],\n                      'bbox': [round(x, 3) for x in b],\n                      'score': round(p[4], 5)})\n\n\ndef process_batch(detections, labels, iouv):\n    \"\"\"\n    Return correct predictions matrix. Both sets of boxes are in (x1, y1, x2, y2) format.\n    Arguments:\n        detections (Array[N, 6]), x1, y1, x2, y2, conf, class\n        labels (Array[M, 5]), class, x1, y1, x2, y2\n    Returns:\n        correct (Array[N, 10]), for 10 IoU levels\n    \"\"\"\n    correct = torch.zeros(detections.shape[0], iouv.shape[0], dtype=torch.bool, device=iouv.device)\n    iou = box_iou(labels[:, 1:], detections[:, :4])\n    x = torch.where((iou >= iouv[0]) & (labels[:, 0:1] == detections[:, 5]))  # IoU above threshold and classes match\n    if x[0].shape[0]:\n        matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy()  # [label, detection, iou]\n        if x[0].shape[0] > 1:\n            matches = matches[matches[:, 2].argsort()[::-1]]\n            matches = matches[np.unique(matches[:, 1], return_index=True)[1]]\n            # matches = matches[matches[:, 2].argsort()[::-1]]\n            matches = matches[np.unique(matches[:, 0], return_index=True)[1]]\n        matches = torch.Tensor(matches).to(iouv.device)\n        correct[matches[:, 1].long()] = matches[:, 2:3] >= iouv\n    return correct\n\n\n@torch.no_grad()\ndef run(data,\n        weights=None,  # model.pt path(s)\n        batch_size=32,  # batch size\n        imgsz=640,  # inference size (pixels)\n        conf_thres=0.001,  # confidence threshold\n        iou_thres=0.6,  # NMS IoU threshold\n        task='val',  # train, val, test, speed or study\n        device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu\n        workers=8,  # max dataloader workers (per RANK in DDP mode)\n        single_cls=False,  # treat as single-class dataset\n        augment=False,  # augmented inference\n        verbose=True,  # verbose output\n        save_txt=False,  # save results to *.txt\n        save_hybrid=False,  # save label+prediction hybrid results to *.txt\n        save_conf=False,  # save confidences in --save-txt labels\n        save_json=False,  # save a COCO-JSON results file\n        project=ROOT / 'runs/val',  # save to project/name\n        name='exp',  # save to project/name\n        exist_ok=False,  # existing project/name ok, do not increment\n        half=True,  # use FP16 half-precision inference\n        dnn=False,  # use OpenCV DNN for ONNX inference\n        model=None,\n        dataloader=None,\n        save_dir=Path(''),\n        plots=True,\n        callbacks=Callbacks(),\n        compute_loss=None,\n        ):\n    # Initialize/load model and set device\n    training = model is not None\n    if training:  # called by train.py\n        device, pt, jit, engine = next(model.parameters()).device, True, False, False  # get model device, PyTorch model\n\n        half &= device.type != 'cpu'  # half precision only supported on CUDA\n        model.half() if half else model.float()\n    else:  # called directly\n        device = select_device(device, batch_size=batch_size)\n\n        # Directories\n        save_dir = increment_path(Path(project) / name, exist_ok=exist_ok)  # increment run\n        (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True)  # make dir\n\n        # Load model\n        model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data)\n        stride, pt, jit, onnx, engine = model.stride, model.pt, model.jit, model.onnx, model.engine\n        imgsz = check_img_size(imgsz, s=stride)  # check image size\n        half &= (pt or jit or onnx or engine) and device.type != 'cpu'  # FP16 supported on limited backends with CUDA\n        if pt or jit:\n            model.model.half() if half else model.model.float()\n        elif engine:\n            batch_size = model.batch_size\n        else:\n            half = False\n            batch_size = 1  # export.py models default to batch-size 1\n            device = torch.device('cpu')\n            LOGGER.info(f'Forcing --batch-size 1 square inference shape(1,3,{imgsz},{imgsz}) for non-PyTorch backends')\n\n        # Data\n        data = check_dataset(data)  # check\n\n    # Configure\n    model.eval()\n    is_coco = isinstance(data.get('val'), str) and data['val'].endswith('coco/val2017.txt')  # COCO dataset\n    nc = 1 if single_cls else int(data['nc'])  # number of classes\n    # iouv = torch.linspace(0.5, 0.95, 10).to(device)  # iou vector for mAP@0.5:0.95\n    iouv= torch.from_numpy(np.arange(0.3, 0.85, 0.05)).to(device)\n    niou = iouv.numel()\n\n    # Dataloader\n    if not training:\n        model.warmup(imgsz=(1 if pt else batch_size, 3, imgsz, imgsz), half=half)  # warmup\n        pad = 0.0 if task == 'speed' else 0.5\n        task = task if task in ('train', 'val', 'test') else 'val'  # path to train/val/test images\n        dataloader = create_dataloader(data[task], imgsz, batch_size, stride, single_cls, pad=pad, rect=pt,\n                                       workers=workers, prefix=colorstr(f'{task}: '))[0]\n\n    seen = 0\n    confusion_matrix = ConfusionMatrix(nc=nc)\n    names = {k: v for k, v in enumerate(model.names if hasattr(model, 'names') else model.module.names)}\n    class_map = coco80_to_coco91_class() if is_coco else list(range(1000))\n    # s = ('%20s' + '%11s' * 6) % ('Class', 'Images', 'Labels', 'P', 'R', 'mAP@.5', 'mAP@.5:.95')\n    s = ('%20s' + '%11s' * 6) % ('Class', 'Images', 'Labels', 'P', 'R', 'F2', 'mAP@.5')\n    # dt, p, r, f1, mp, mr, map50, map = [0.0, 0.0, 0.0], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0\n    dt, p, r, f2, mp, mr, map50, map = [0.0, 0.0, 0.0], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0\n    loss = torch.zeros(3, device=device)\n    jdict, stats, ap, ap_class = [], [], [], []\n    pbar = tqdm(dataloader, desc=s, bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}')  # progress bar\n    for batch_i, (im, targets, paths, shapes) in enumerate(pbar):\n        t1 = time_sync()\n        if pt or jit or engine:\n            im = im.to(device, non_blocking=True)\n            targets = targets.to(device)\n        im = im.half() if half else im.float()  # uint8 to fp16/32\n        im /= 255  # 0 - 255 to 0.0 - 1.0\n        nb, _, height, width = im.shape  # batch size, channels, height, width\n        t2 = time_sync()\n        dt[0] += t2 - t1\n\n        # Inference\n        out, train_out = model(im) if training else model(im, augment=augment, val=True)  # inference, loss outputs\n        dt[1] += time_sync() - t2\n\n        # Loss\n        if compute_loss:\n            loss += compute_loss([x.float() for x in train_out], targets)[1]  # box, obj, cls\n\n        # NMS\n        targets[:, 2:] *= torch.Tensor([width, height, width, height]).to(device)  # to pixels\n        lb = [targets[targets[:, 0] == i, 1:] for i in range(nb)] if save_hybrid else []  # for autolabelling\n        t3 = time_sync()\n        out = non_max_suppression(out, conf_thres, iou_thres, labels=lb, multi_label=True, agnostic=single_cls)\n        dt[2] += time_sync() - t3\n\n        # Metrics\n        for si, pred in enumerate(out):\n            labels = targets[targets[:, 0] == si, 1:]\n            nl = len(labels)\n            tcls = labels[:, 0].tolist() if nl else []  # target class\n            path, shape = Path(paths[si]), shapes[si][0]\n            seen += 1\n\n            if len(pred) == 0:\n                if nl:\n                    stats.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls))\n                continue\n\n            # Predictions\n            if single_cls:\n                pred[:, 5] = 0\n            predn = pred.clone()\n            scale_coords(im[si].shape[1:], predn[:, :4], shape, shapes[si][1])  # native-space pred\n\n            # Evaluate\n            if nl:\n                tbox = xywh2xyxy(labels[:, 1:5])  # target boxes\n                scale_coords(im[si].shape[1:], tbox, shape, shapes[si][1])  # native-space labels\n                labelsn = torch.cat((labels[:, 0:1], tbox), 1)  # native-space labels\n                correct = process_batch(predn, labelsn, iouv)\n                if plots:\n                    confusion_matrix.process_batch(predn, labelsn)\n            else:\n                correct = torch.zeros(pred.shape[0], niou, dtype=torch.bool)\n            stats.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls))  # (correct, conf, pcls, tcls)\n\n            # Save/log\n            if save_txt:\n                save_one_txt(predn, save_conf, shape, file=save_dir / 'labels' / (path.stem + '.txt'))\n            if save_json:\n                save_one_json(predn, jdict, path, class_map)  # append to COCO-JSON dictionary\n            callbacks.run('on_val_image_end', pred, predn, path, names, im[si])\n\n        # Plot images\n        if plots and batch_i < 3:\n            f = save_dir / f'val_batch{batch_i}_labels.jpg'  # labels\n            Thread(target=plot_images, args=(im, targets, paths, f, names), daemon=True).start()\n            f = save_dir / f'val_batch{batch_i}_pred.jpg'  # predictions\n            Thread(target=plot_images, args=(im, output_to_target(out), paths, f, names), daemon=True).start()\n\n    # Compute metrics\n    stats = [np.concatenate(x, 0) for x in zip(*stats)]  # to numpy\n    if len(stats) and stats[0].any():\n        # tp, fp, p, r, f1, ap, ap_class = ap_per_class(*stats, plot=plots, save_dir=save_dir, names=names)\n        tp, fp, p, r, f2, ap, ap_class = ap_per_class(*stats, plot=plots, save_dir=save_dir, names=names)\n        # ap50, ap = ap[:, 0], ap.mean(1)  # AP@0.5, AP@0.5:0.95\n        # mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()\n        ap50, ap, f2 = ap[:, 0], ap.mean(1), f2.mean(0)  # AP@0.5, AP@0.5:0.95\n        mp, mr, f2, map50, map = p.mean(), r.mean(), f2.mean(), ap50.mean(), ap.mean()\n        nt = np.bincount(stats[3].astype(np.int64), minlength=nc)  # number of targets per class\n    else:\n        nt = torch.zeros(1)\n\n    # Print results\n    pf = '%20s' + '%11i' * 2 + '%11.3g' * 4  # print format\n    # LOGGER.info(pf % ('all', seen, nt.sum(), mp, mr, map50, map))\n    LOGGER.info(pf % ('all', seen, nt.sum(), mp, mr, f2, map50))\n    \n    # Print results per class\n    if (verbose or (nc < 50 and not training)) and nc > 1 and len(stats):\n        for i, c in enumerate(ap_class):\n            LOGGER.info(pf % (names[c], seen, nt[c], p[i], r[i], ap50[i], ap[i]))\n\n    # Print speeds\n    t = tuple(x / seen * 1E3 for x in dt)  # speeds per image\n    if not training:\n        shape = (batch_size, 3, imgsz, imgsz)\n        LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {shape}' % t)\n\n    # Plots\n    if plots:\n        confusion_matrix.plot(save_dir=save_dir, names=list(names.values()))\n        callbacks.run('on_val_end')\n\n    # Save JSON\n    if save_json and len(jdict):\n        w = Path(weights[0] if isinstance(weights, list) else weights).stem if weights is not None else ''  # weights\n        anno_json = str(Path(data.get('path', '../coco')) / 'annotations/instances_val2017.json')  # annotations json\n        pred_json = str(save_dir / f\"{w}_predictions.json\")  # predictions json\n        LOGGER.info(f'\\nEvaluating pycocotools mAP... saving {pred_json}...')\n        with open(pred_json, 'w') as f:\n            json.dump(jdict, f)\n\n        try:  # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb\n            check_requirements(['pycocotools'])\n            from pycocotools.coco import COCO\n            from pycocotools.cocoeval import COCOeval\n\n            anno = COCO(anno_json)  # init annotations api\n            pred = anno.loadRes(pred_json)  # init predictions api\n            eval = COCOeval(anno, pred, 'bbox')\n            if is_coco:\n                eval.params.imgIds = [int(Path(x).stem) for x in dataloader.dataset.img_files]  # image IDs to evaluate\n            eval.evaluate()\n            eval.accumulate()\n            eval.summarize()\n            map, map50 = eval.stats[:2]  # update results (mAP@0.5:0.95, mAP@0.5)\n        except Exception as e:\n            LOGGER.info(f'pycocotools unable to run: {e}')\n\n    # Return results\n    model.float()  # for training\n    if not training:\n        s = f\"\\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}\" if save_txt else ''\n        LOGGER.info(f\"Results saved to {colorstr('bold', save_dir)}{s}\")\n    maps = np.zeros(nc) + map\n    for i, c in enumerate(ap_class):\n        maps[c] = ap[i]\n    # return (mp, mr, map50, map, *(loss.cpu() / len(dataloader)).tolist()), maps, t\n    return (mp, mr, map50, map, f2, *(loss.cpu() / len(dataloader)).tolist()), maps, t\n\ndef parse_opt():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='dataset.yaml path')\n    parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s.pt', help='model.pt path(s)')\n    parser.add_argument('--batch-size', type=int, default=32, help='batch size')\n    parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='inference size (pixels)')\n    parser.add_argument('--conf-thres', type=float, default=0.001, help='confidence threshold')\n    parser.add_argument('--iou-thres', type=float, default=0.6, help='NMS IoU threshold')\n    parser.add_argument('--task', default='val', help='train, val, test, speed or study')\n    parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')\n    parser.add_argument('--workers', type=int, default=8, help='max dataloader workers (per RANK in DDP mode)')\n    parser.add_argument('--single-cls', action='store_true', help='treat as single-class dataset')\n    parser.add_argument('--augment', action='store_true', help='augmented inference')\n    parser.add_argument('--verbose', action='store_true', help='report mAP by class')\n    parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')\n    parser.add_argument('--save-hybrid', action='store_true', help='save label+prediction hybrid results to *.txt')\n    parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')\n    parser.add_argument('--save-json', action='store_true', help='save a COCO-JSON results file')\n    parser.add_argument('--project', default=ROOT / 'runs/val', help='save to project/name')\n    parser.add_argument('--name', default='exp', help='save to project/name')\n    parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')\n    parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference')\n    parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference')\n    opt = parser.parse_args()\n    opt.data = check_yaml(opt.data)  # check YAML\n    opt.save_json |= opt.data.endswith('coco.yaml')\n    opt.save_txt |= opt.save_hybrid\n    print_args(FILE.stem, opt)\n    return opt\n\n\ndef main(opt):\n    check_requirements(requirements=ROOT / 'requirements.txt', exclude=('tensorboard', 'thop'))\n\n    if opt.task in ('train', 'val', 'test'):  # run normally\n        if opt.conf_thres > 0.001:  # https://github.com/ultralytics/yolov5/issues/1466\n            LOGGER.info(f'WARNING: confidence threshold {opt.conf_thres} >> 0.001 will produce invalid mAP values.')\n        run(**vars(opt))\n\n    else:\n        weights = opt.weights if isinstance(opt.weights, list) else [opt.weights]\n        opt.half = True  # FP16 for fastest results\n        if opt.task == 'speed':  # speed benchmarks\n            # python val.py --task speed --data coco.yaml --batch 1 --weights yolov5n.pt yolov5s.pt...\n            opt.conf_thres, opt.iou_thres, opt.save_json = 0.25, 0.45, False\n            for opt.weights in weights:\n                run(**vars(opt), plots=False)\n\n        elif opt.task == 'study':  # speed vs mAP benchmarks\n            # python val.py --task study --data coco.yaml --iou 0.7 --weights yolov5n.pt yolov5s.pt...\n            for opt.weights in weights:\n                f = f'study_{Path(opt.data).stem}_{Path(opt.weights).stem}.txt'  # filename to save to\n                x, y = list(range(256, 1536 + 128, 128)), []  # x axis (image sizes), y axis\n                for opt.imgsz in x:  # img-size\n                    LOGGER.info(f'\\nRunning {f} --imgsz {opt.imgsz}...')\n                    r, _, t = run(**vars(opt), plots=False)\n                    y.append(r + t)  # results and times\n                np.savetxt(f, y, fmt='%10.4g')  # save\n            os.system('zip -r study.zip study_*.txt')\n            plot_val_study(x=x)  # plot\n\n\nif __name__ == \"__main__\":\n    opt = parse_opt()\n    main(opt)\n","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:30.942404Z","iopub.execute_input":"2022-02-10T21:07:30.942915Z","iopub.status.idle":"2022-02-10T21:07:30.963094Z","shell.execute_reply.started":"2022-02-10T21:07:30.942853Z","shell.execute_reply":"2022-02-10T21:07:30.962184Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile /kaggle/working/yolov5/utils/loggers/__init__.py\n# YOLOv5 🚀 by Ultralytics, GPL-3.0 license\n\"\"\"\nLogging utils\n\"\"\"\n\nimport os\nimport warnings\nfrom threading import Thread\n\nimport pkg_resources as pkg\nimport torch\nfrom torch.utils.tensorboard import SummaryWriter\n\nfrom utils.general import colorstr, emojis\nfrom utils.loggers.wandb.wandb_utils import WandbLogger\nfrom utils.plots import plot_images, plot_results\nfrom utils.torch_utils import de_parallel\n\nLOGGERS = ('csv', 'tb', 'wandb')  # text-file, TensorBoard, Weights & Biases\nRANK = int(os.getenv('RANK', -1))\n\ntry:\n    import wandb\n\n    assert hasattr(wandb, '__version__')  # verify package import not local dir\n    if pkg.parse_version(wandb.__version__) >= pkg.parse_version('0.12.2') and RANK in [0, -1]:\n        try:\n            wandb_login_success = wandb.login(timeout=30)\n        except wandb.errors.UsageError:  # known non-TTY terminal issue\n            wandb_login_success = False\n        if not wandb_login_success:\n            wandb = None\nexcept (ImportError, AssertionError):\n    wandb = None\n\n\nclass Loggers():\n    # YOLOv5 Loggers class\n    def __init__(self, save_dir=None, weights=None, opt=None, hyp=None, logger=None, include=LOGGERS):\n        self.save_dir = save_dir\n        self.weights = weights\n        self.opt = opt\n        self.hyp = hyp\n        self.logger = logger  # for printing results to console\n        self.include = include\n        self.keys = ['train/box_loss', 'train/obj_loss', 'train/cls_loss',  # train loss\n                     'metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95', 'metrics/F2', # metrics\n                     'val/box_loss', 'val/obj_loss', 'val/cls_loss',  # val loss\n                     'x/lr0', 'x/lr1', 'x/lr2']  # params\n        self.best_keys = ['best/epoch', 'best/precision', 'best/recall', 'best/F2', 'best/mAP_0.5', 'best/mAP_0.5:0.95',]\n        for k in LOGGERS:\n            setattr(self, k, None)  # init empty logger dictionary\n        self.csv = True  # always log to csv\n\n        # Message\n        if not wandb:\n            prefix = colorstr('Weights & Biases: ')\n            s = f\"{prefix}run 'pip install wandb' to automatically track and visualize YOLOv5 🚀 runs (RECOMMENDED)\"\n            print(emojis(s))\n\n        # TensorBoard\n        s = self.save_dir\n        if 'tb' in self.include and not self.opt.evolve:\n            prefix = colorstr('TensorBoard: ')\n            self.logger.info(f\"{prefix}Start with 'tensorboard --logdir {s.parent}', view at http://localhost:6006/\")\n            self.tb = SummaryWriter(str(s))\n\n        # W&B\n        if wandb and 'wandb' in self.include:\n            wandb_artifact_resume = isinstance(self.opt.resume, str) and self.opt.resume.startswith('wandb-artifact://')\n            run_id = torch.load(self.weights).get('wandb_id') if self.opt.resume and not wandb_artifact_resume else None\n            self.opt.hyp = self.hyp  # add hyperparameters\n            self.wandb = WandbLogger(self.opt, run_id)\n        else:\n            self.wandb = None\n\n    def on_pretrain_routine_end(self):\n        # Callback runs on pre-train routine end\n        paths = self.save_dir.glob('*labels*.jpg')  # training labels\n        if self.wandb:\n            self.wandb.log({\"Labels\": [wandb.Image(str(x), caption=x.name) for x in paths]})\n\n    def on_train_batch_end(self, ni, model, imgs, targets, paths, plots, sync_bn):\n        # Callback runs on train batch end\n        if plots:\n            if ni == 0:\n                if not sync_bn:  # tb.add_graph() --sync known issue https://github.com/ultralytics/yolov5/issues/3754\n                    with warnings.catch_warnings():\n                        warnings.simplefilter('ignore')  # suppress jit trace warning\n                        self.tb.add_graph(torch.jit.trace(de_parallel(model), imgs[0:1], strict=False), [])\n            if ni < 3:\n                f = self.save_dir / f'train_batch{ni}.jpg'  # filename\n                Thread(target=plot_images, args=(imgs, targets, paths, f), daemon=True).start()\n            if self.wandb and ni == 10:\n                files = sorted(self.save_dir.glob('train*.jpg'))\n                self.wandb.log({'Mosaics': [wandb.Image(str(f), caption=f.name) for f in files if f.exists()]})\n\n    def on_train_epoch_end(self, epoch):\n        # Callback runs on train epoch end\n        if self.wandb:\n            self.wandb.current_epoch = epoch + 1\n\n    def on_val_image_end(self, pred, predn, path, names, im):\n        # Callback runs on val image end\n        if self.wandb:\n            self.wandb.val_one_image(pred, predn, path, names, im)\n\n    def on_val_end(self):\n        # Callback runs on val end\n        if self.wandb:\n            files = sorted(self.save_dir.glob('val*.jpg'))\n            self.wandb.log({\"Validation\": [wandb.Image(str(f), caption=f.name) for f in files]})\n\n    def on_fit_epoch_end(self, vals, epoch, best_fitness, fi):\n        # Callback runs at the end of each fit (train+val) epoch\n        x = {k: v for k, v in zip(self.keys, vals)}  # dict\n        if self.csv:\n            file = self.save_dir / 'results.csv'\n            n = len(x) + 1  # number of cols\n            s = '' if file.exists() else (('%20s,' * n % tuple(['epoch'] + self.keys)).rstrip(',') + '\\n')  # add header\n            with open(file, 'a') as f:\n                f.write(s + ('%20.5g,' * n % tuple([epoch] + vals)).rstrip(',') + '\\n')\n\n        if self.tb:\n            for k, v in x.items():\n                self.tb.add_scalar(k, v, epoch)\n\n        if self.wandb:\n            if best_fitness == fi:\n                # best_results = [epoch] + vals[3:7]\n                best_results = [epoch] + vals[3:8]\n                for i, name in enumerate(self.best_keys):\n                    self.wandb.wandb_run.summary[name] = best_results[i]  # log best results in the summary\n            self.wandb.log(x)\n            self.wandb.end_epoch(best_result=best_fitness == fi)\n\n    def on_model_save(self, last, epoch, final_epoch, best_fitness, fi):\n        # Callback runs on model save event\n        if self.wandb:\n            if ((epoch + 1) % self.opt.save_period == 0 and not final_epoch) and self.opt.save_period != -1:\n                self.wandb.log_model(last.parent, self.opt, epoch, fi, best_model=best_fitness == fi)\n\n    def on_train_end(self, last, best, plots, epoch, results):\n        # Callback runs on training end\n        if plots:\n            plot_results(file=self.save_dir / 'results.csv')  # save results.png\n        files = ['results.png', 'confusion_matrix.png', *(f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R'))]\n        files = [(self.save_dir / f) for f in files if (self.save_dir / f).exists()]  # filter\n\n        if self.tb:\n            import cv2\n            for f in files:\n                self.tb.add_image(f.stem, cv2.imread(str(f))[..., ::-1], epoch, dataformats='HWC')\n\n        if self.wandb:\n            self.wandb.log({k: v for k, v in zip(self.keys[3:10], results)})  # log best.pt val results\n            self.wandb.log({\"Results\": [wandb.Image(str(f), caption=f.name) for f in files]})\n            # Calling wandb.log. TODO: Refactor this into WandbLogger.log_model\n            if not self.opt.evolve:\n                wandb.log_artifact(str(best if best.exists() else last), type='model',\n                                   name='run_' + self.wandb.wandb_run.id + '_model',\n                                   aliases=['latest', 'best', 'stripped'])\n            self.wandb.finish_run()\n\n    def on_params_update(self, params):\n        # Update hyperparams or configs of the experiment\n        # params: A dict containing {param: value} pairs\n        if self.wandb:\n            self.wandb.wandb_run.config.update(params, allow_val_change=True)","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:30.964816Z","iopub.execute_input":"2022-02-10T21:07:30.965155Z","iopub.status.idle":"2022-02-10T21:07:30.978949Z","shell.execute_reply.started":"2022-02-10T21:07:30.965116Z","shell.execute_reply":"2022-02-10T21:07:30.977902Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"### rain YOLOv5s on COCO128 for 3 epochs\n# !python train.py --img 1600 --batch-size 2 --epochs 10 --data tf_cots_dataset.yaml --weights yolov5x.pt --hyp /kaggle/working/yolov5/data/hyps/hyp.scratch.yaml --optimizer Adam --patience 5 --save-period 1\n### rain YOLOv5s on COCO128 for 3 epochs\n\n!python train.py --img 1600 --batch-size 4 --epochs 15 --data tf_cots_dataset.yaml --weights yolov5l6.pt --hyp /kaggle/working/yolov5/data/hyps/hyp.scratch.yaml --optimizer Adam --patience 5 --save-period 1\n# !python train.py --img 1920 --batch-size 8 --epochs 15 --data tf_cots_dataset.yaml --weights yolov5s-transformer.pt --hyp /kaggle/working/yolov5/data/hyps/hyp.scratch.yaml --optimizer Adam --patience 5 --save-period 1","metadata":{"execution":{"iopub.status.busy":"2022-02-10T21:07:30.980519Z","iopub.execute_input":"2022-02-10T21:07:30.980906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%ls /kaggle/working/yolov5/runs/train/exp","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from PIL import Image\nimport matplotlib.pyplot as plt\n\n%matplotlib inline","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(36, 24))\nplt.imshow(Image.open(f'/kaggle/working/yolov5/runs/train/exp/results.png'))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(36, 24))\nplt.imshow(Image.open(f'/kaggle/working/yolov5/runs/train/exp/F2_curve.png'))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(36, 24))\nplt.imshow(Image.open(f'/kaggle/working/yolov5/runs/train/exp/PR_curve.png'))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(36, 24))\nplt.imshow(Image.open(f'/kaggle/working/yolov5/runs/train/exp/train_batch1.jpg'))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(36, 24))\nplt.imshow(Image.open(f'/kaggle/working/yolov5/runs/train/exp/val_batch1_labels.jpg'))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(36, 24))\nplt.imshow(Image.open(f'/kaggle/working/yolov5/runs/train/exp/val_batch1_pred.jpg'))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(36, 24))\nplt.imshow(Image.open(f'/kaggle/working/yolov5/runs/train/exp/confusion_matrix.png'))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}